private updateConnections()

in nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/flow-designer/service/manager/connection-manager.service.ts [909:1942]


    private updateConnections(updated: any, { updatePath = true, updateLabel = true }: ConnectionRenderOptions = {}) {
        if (updated.empty()) {
            return;
        }

        const self: ConnectionManager = this;
        if (updatePath) {
            updated
                .classed('grouped', function (d: any) {
                    let grouped = false;

                    if (d.permissions.canRead) {
                        // if there are more than one selected relationship, mark this as grouped
                        if (d.component.selectedRelationships?.length > 1) {
                            grouped = true;
                        }
                    }

                    return grouped;
                })
                .classed('ghost', function (d: any) {
                    let ghost = false;

                    if (d.permissions.canRead) {
                        // if the connection has a relationship that is unavailable, mark it a ghost relationship
                        if (self.hasUnavailableRelationship(d)) {
                            ghost = true;
                        }
                    }

                    return ghost;
                });

            // update connection path
            updated.select('path.connection-path').classed('unauthorized', function (d: any) {
                return d.permissions.canRead === false;
            });
        }

        updated.each(function (this: any, d: any) {
            const connection: any = d3.select(this);

            if (updatePath) {
                // calculate the start and end points
                const sourceComponentId: string = self.canvasUtils.getConnectionSourceComponentId(d);
                const sourceData: any = d3.select('#id-' + sourceComponentId).datum();
                let end: Position;

                // get the appropriate end anchor point
                let endAnchor: any;
                if (d.bends.length > 0) {
                    endAnchor = d.bends[d.bends.length - 1];
                } else {
                    endAnchor = {
                        x: sourceData.position.x + sourceData.dimensions.width / 2,
                        y: sourceData.position.y + sourceData.dimensions.height / 2
                    };
                }

                // if we are currently dragging the endpoint to a new target, use that
                // position, otherwise we need to calculate it for the current target
                if (d.end?.endPointDragging) {
                    // since we're dragging, use the same object thats bound to the endpoint drag event
                    end = d.end;

                    // if we're not over a connectable destination use the current point
                    const newDestination: any = d3.select('g.hover.connectable-destination');
                    if (!newDestination.empty()) {
                        const newDestinationData: any = newDestination.datum();

                        // get the position on the new destination perimeter
                        const newEnd: Position = self.canvasUtils.getPerimeterPoint(endAnchor, {
                            x: newDestinationData.position.x,
                            y: newDestinationData.position.y,
                            width: newDestinationData.dimensions.width,
                            height: newDestinationData.dimensions.height
                        });

                        // update the coordinates with the new point
                        end.x = newEnd.x;
                        end.y = newEnd.y;
                    }
                } else {
                    const destinationComponentId: string = self.canvasUtils.getConnectionDestinationComponentId(d);
                    const destinationData: any = d3.select('#id-' + destinationComponentId).datum();

                    // get the position on the destination perimeter
                    end = self.canvasUtils.getPerimeterPoint(endAnchor, {
                        x: destinationData.position.x,
                        y: destinationData.position.y,
                        width: destinationData.dimensions.width,
                        height: destinationData.dimensions.height
                    });
                }

                // get the appropriate start anchor point
                let startAnchor: Position;
                if (d.bends.length > 0) {
                    startAnchor = d.bends[0];
                } else {
                    startAnchor = end;
                }

                // get the position on the source perimeter
                const start: Position = self.canvasUtils.getPerimeterPoint(startAnchor, {
                    x: sourceData.position.x,
                    y: sourceData.position.y,
                    width: sourceData.dimensions.width,
                    height: sourceData.dimensions.height
                });

                // store the updated endpoints
                d.start = start;
                d.end = end;

                // update the connection paths
                self.transitionBehavior
                    .transition(connection.select('path.connection-path'), self.transitionRequired)
                    .attr('d', function () {
                        const datum: Position[] = [d.start].concat(d.bends, [d.end]);
                        return self.lineGenerator(datum);
                    });
                self.transitionBehavior
                    .transition(connection.select('path.connection-selection-path'), self.transitionRequired)
                    .attr('d', function () {
                        const datum: Position[] = [d.start].concat(d.bends, [d.end]);
                        return self.lineGenerator(datum);
                    });
                self.transitionBehavior
                    .transition(connection.select('path.connection-path-selectable'), self.transitionRequired)
                    .attr('d', function () {
                        const datum: Position[] = [d.start].concat(d.bends, [d.end]);
                        return self.lineGenerator(datum);
                    });

                // -----
                // bends
                // -----

                let startpoints: any = connection.selectAll('rect.startpoint');
                let endpoints: any = connection.selectAll('rect.endpoint');
                let midpoints: any = connection.selectAll('rect.midpoint');

                // require read and write permissions as it's required to read the connections available relationships
                // when connecting to a group or remote group
                if (d.permissions.canWrite && d.permissions.canRead) {
                    // ------------------
                    // bends - startpoint
                    // ------------------

                    startpoints = startpoints.data([d.start]);

                    // create a point for the start
                    const startpointsEntered: any = startpoints
                        .enter()
                        .append('rect')
                        .attr('class', 'startpoint linepoint')
                        .attr('pointer-events', 'all')
                        .attr('width', 8)
                        .attr('height', 8)
                        .on('mousedown.selection', function (this: any, event: MouseEvent) {
                            // select the connection when clicking the label
                            self.selectableBehavior.select(event, d3.select(this.parentNode));
                        });

                    // update the start point
                    self.transitionBehavior
                        .transition(startpoints.merge(startpointsEntered), self.transitionRequired)
                        .attr('transform', function (p: any) {
                            return 'translate(' + (p.x - 4) + ', ' + (p.y - 4) + ')';
                        });

                    // remove old items
                    startpoints.exit().remove();

                    // ----------------
                    // bends - endpoint
                    // ----------------

                    endpoints = endpoints.data([d.end]);

                    // create a point for the end
                    const endpointsEntered = endpoints
                        .enter()
                        .append('rect')
                        .attr('class', 'endpoint linepoint')
                        .attr('pointer-events', 'all')
                        .attr('width', 8)
                        .attr('height', 8)
                        .on('mousedown.selection', function (this: any, event: MouseEvent) {
                            // select the connection when clicking the label
                            self.selectableBehavior.select(event, d3.select(this.parentNode));
                        })
                        .call(self.endpointDrag);

                    // update the end point
                    self.transitionBehavior
                        .transition(endpoints.merge(endpointsEntered), self.transitionRequired)
                        .attr('transform', function (p: any) {
                            return 'translate(' + (p.x - 4) + ', ' + (p.y - 4) + ')';
                        });

                    // remove old items
                    endpoints.exit().remove();

                    // -----------------
                    // bends - midpoints
                    // -----------------

                    midpoints = midpoints.data(d.bends);

                    // create a point for the end
                    const midpointsEntered = midpoints
                        .enter()
                        .append('rect')
                        .attr('class', 'midpoint linepoint')
                        .attr('pointer-events', 'all')
                        .attr('width', 8)
                        .attr('height', 8)
                        .on('dblclick', function (this: any, event: MouseEvent, p: any) {
                            // stop even propagation
                            event.stopPropagation();

                            const connection: any = d3.select(this.parentNode);
                            const connectionData: any = connection.datum();

                            // if this is a self loop prevent removing the last two bends
                            const sourceComponentId = self.canvasUtils.getConnectionSourceComponentId(connectionData);
                            const destinationComponentId =
                                self.canvasUtils.getConnectionDestinationComponentId(connectionData);
                            if (sourceComponentId === destinationComponentId && d.component.bends.length <= 2) {
                                this.store.dispatch(
                                    showOkDialog({
                                        title: 'Connection',
                                        message: 'Looping connections must have at least two bend points.'
                                    })
                                );
                                return;
                            }

                            const newBends: any[] = [];
                            let bendIndex = -1;

                            // create a new array of bends without the selected one
                            connectionData.component.bends.forEach((bend: any, i: number) => {
                                if (p.x !== bend.x && p.y !== bend.y) {
                                    newBends.push(bend);
                                } else {
                                    bendIndex = i;
                                }
                            });

                            if (bendIndex < 0) {
                                return;
                            }

                            const connectionRemovedBend: any = {
                                id: connectionData.id,
                                bends: newBends
                            };

                            // update the label index if necessary
                            const labelIndex: number = connectionData.component.labelIndex;
                            if (newBends.length <= 1) {
                                connectionRemovedBend.labelIndex = 0;
                            } else if (bendIndex <= labelIndex) {
                                connectionRemovedBend.labelIndex = Math.max(0, labelIndex - 1);
                            }

                            // save the updated connection
                            self.save(connectionData, connectionRemovedBend);
                        })
                        .on('mousedown.selection', function (this: any, event: MouseEvent) {
                            // select the connection when clicking the label
                            self.selectableBehavior.select(event, d3.select(this.parentNode));
                        })
                        .call(self.bendPointDrag);

                    // update the midpoints
                    self.transitionBehavior
                        .transition(midpoints.merge(midpointsEntered), self.transitionRequired)
                        .attr('transform', function (p: any) {
                            return 'translate(' + (p.x - 4) + ', ' + (p.y - 4) + ')';
                        });

                    // remove old items
                    midpoints.exit().remove();
                } else {
                    // remove the start, mid, and end points
                    startpoints.remove();
                    endpoints.remove();
                    midpoints.remove();
                }
            }

            if (updateLabel) {
                let connectionLabelContainer: any = connection.select('g.connection-label-container');

                // update visible connections
                if (connection.classed('visible')) {
                    // if there is no connection label this connection is becoming
                    // visible so we need to render it
                    if (connectionLabelContainer.empty()) {
                        // connection label container
                        connectionLabelContainer = connection
                            .insert('g', 'rect.startpoint')
                            .attr('class', 'connection-label-container')
                            .attr('pointer-events', 'all')
                            .on('mousedown.selection', function (this: any, event: MouseEvent) {
                                // select the connection when clicking the label
                                self.selectableBehavior.select(event, d3.select(this.parentNode));
                            });

                        self.quickSelectBehavior.activate(connectionLabelContainer);

                        // connection label
                        connectionLabelContainer
                            .append('rect')
                            .attr('class', 'body')
                            .attr('width', ConnectionManager.DIMENSIONS.width)
                            .attr('x', 0)
                            .attr('y', 0);

                        // processor border
                        connectionLabelContainer
                            .append('rect')
                            .attr('class', 'border')
                            .attr('width', ConnectionManager.DIMENSIONS.width)
                            .attr('fill', 'transparent')
                            .attr('stroke', 'transparent');
                    }

                    let labelCount = 0;
                    const rowHeight = 19;
                    const backgrounds: any[] = [];
                    const borders: any[] = [];

                    let connectionFrom = connectionLabelContainer.select('g.connection-from-container');
                    let connectionTo = connectionLabelContainer.select('g.connection-to-container');
                    let connectionName = connectionLabelContainer.select('g.connection-name-container');

                    if (d.permissions.canRead) {
                        // -----------------------
                        // connection label - from
                        // -----------------------

                        // determine if the connection require a from label
                        if (self.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');

                                // background
                                backgrounds.push(
                                    connectionFrom
                                        .append('rect')
                                        .attr('class', 'connection-label-background')
                                        .attr('width', ConnectionManager.DIMENSIONS.width)
                                        .attr('height', rowHeight)
                                );

                                // border
                                borders.push(
                                    connectionFrom
                                        .append('rect')
                                        .attr('class', 'connection-label-border')
                                        .attr('width', ConnectionManager.DIMENSIONS.width)
                                        .attr('height', 1)
                                );

                                connectionFrom
                                    .append('text')
                                    .attr('class', 'stats-label')
                                    .attr('x', 5)
                                    .attr('y', 14)
                                    .text('From');

                                connectionFrom
                                    .append('text')
                                    .attr('class', 'stats-value connection-from')
                                    .attr('x', 43)
                                    .attr('y', 14)
                                    .attr('width', 130);

                                connectionFrom
                                    .append('text')
                                    .attr('class', 'connection-from-run-status')
                                    .attr('x', 208)
                                    .attr('y', 14);
                            } else {
                                backgrounds.push(connectionFrom.select('rect.connection-label-background'));
                                borders.push(connectionFrom.select('rect.connection-label-border'));
                            }

                            // update the connection from positioning
                            connectionFrom.attr('transform', function () {
                                const y: number = rowHeight * labelCount++;
                                return 'translate(0, ' + y + ')';
                            });

                            // update the label text
                            connectionFrom
                                .select('text.connection-from')
                                .each(function (this: any) {
                                    const 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
                                    self.canvasUtils.ellipsis(
                                        connectionFromLabel,
                                        d.component.source.name,
                                        'connection-from'
                                    );
                                })
                                .append('title')
                                .text(function () {
                                    return d.component.source.name;
                                });

                            // update the label run status
                            connectionFrom
                                .select('text.connection-from-run-status')
                                .text(function () {
                                    if (d.component.source.exists === false) {
                                        return '\uf071';
                                    } else if (d.component.source.running === true) {
                                        return '\uf04b';
                                    } else {
                                        return '\uf04d';
                                    }
                                })
                                .classed('running success-color-default', function () {
                                    if (d.component.source.exists === false) {
                                        return false;
                                    } else {
                                        return d.component.source.running;
                                    }
                                })
                                .classed('stopped error-color-variant', function () {
                                    if (d.component.source.exists === false) {
                                        return false;
                                    } else {
                                        return !d.component.source.running;
                                    }
                                })
                                .classed('is-missing-port invalid caution-color', function () {
                                    return d.component.source.exists === false;
                                });
                        } else {
                            // there is no connection from, remove the previous if necessary
                            connectionFrom.remove();
                        }

                        // ---------------------
                        // connection label - to
                        // ---------------------

                        // determine if the connection require a to label
                        if (self.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');

                                // background
                                backgrounds.push(
                                    connectionTo
                                        .append('rect')
                                        .attr('class', 'connection-label-background')
                                        .attr('width', ConnectionManager.DIMENSIONS.width)
                                        .attr('height', rowHeight)
                                );

                                // border
                                borders.push(
                                    connectionTo
                                        .append('rect')
                                        .attr('class', 'connection-label-border')
                                        .attr('width', ConnectionManager.DIMENSIONS.width)
                                        .attr('height', 1)
                                );

                                connectionTo
                                    .append('text')
                                    .attr('class', 'stats-label')
                                    .attr('x', 5)
                                    .attr('y', 14)
                                    .text('To');

                                connectionTo
                                    .append('text')
                                    .attr('class', 'stats-value connection-to')
                                    .attr('x', 25)
                                    .attr('y', 14)
                                    .attr('width', 145);

                                connectionTo
                                    .append('text')
                                    .attr('class', 'connection-to-run-status')
                                    .attr('x', 208)
                                    .attr('y', 14);
                            } else {
                                backgrounds.push(connectionTo.select('rect.connection-label-background'));
                                borders.push(connectionTo.select('rect.connection-label-border'));
                            }

                            // update the connection to positioning
                            connectionTo.attr('transform', function () {
                                const y: number = rowHeight * labelCount++;
                                return 'translate(0, ' + y + ')';
                            });

                            // update the label text
                            connectionTo
                                .select('text.connection-to')
                                .each(function (this: any, d: any) {
                                    const 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
                                    self.canvasUtils.ellipsis(
                                        connectionToLabel,
                                        d.component.destination.name,
                                        'connection-to'
                                    );
                                })
                                .append('title')
                                .text(function (d: any) {
                                    return d.component.destination.name;
                                });

                            // update the label run status
                            connectionTo
                                .select('text.connection-to-run-status')
                                .text(function () {
                                    if (d.component.destination.exists === false) {
                                        return '\uf071';
                                    } else if (d.component.destination.running === true) {
                                        return '\uf04b';
                                    } else {
                                        return '\uf04d';
                                    }
                                })
                                .classed('running success-color-default', function () {
                                    if (d.component.destination.exists === false) {
                                        return false;
                                    } else {
                                        return d.component.destination.running;
                                    }
                                })
                                .classed('stopped error-color-variant', function () {
                                    if (d.component.destination.exists === false) {
                                        return false;
                                    } else {
                                        return !d.component.destination.running;
                                    }
                                })
                                .classed('is-missing-port invalid caution-color', function () {
                                    return d.component.destination.exists === false;
                                });
                        } else {
                            // there is no connection to, remove the previous if necessary
                            connectionTo.remove();
                        }

                        // -----------------------
                        // connection label - name
                        // -----------------------

                        // get the connection name
                        const connectionNameValue: string = self.canvasUtils.formatConnectionName(d.component);

                        // is there a name to render
                        if (!self.nifiCommon.isBlank(connectionNameValue)) {
                            // see if the connection name label is already rendered
                            if (connectionName.empty()) {
                                connectionName = connectionLabelContainer
                                    .append('g')
                                    .attr('class', 'connection-name-container');

                                // background
                                backgrounds.push(
                                    connectionName
                                        .append('rect')
                                        .attr('class', 'connection-label-background')
                                        .attr('width', ConnectionManager.DIMENSIONS.width)
                                        .attr('height', rowHeight)
                                );

                                // border
                                borders.push(
                                    connectionName
                                        .append('rect')
                                        .attr('class', 'connection-label-border')
                                        .attr('width', ConnectionManager.DIMENSIONS.width)
                                        .attr('height', 1)
                                );

                                connectionName
                                    .append('text')
                                    .attr('class', 'stats-label')
                                    .attr('x', 5)
                                    .attr('y', 14)
                                    .text('Name');

                                connectionName
                                    .append('text')
                                    .attr('class', 'stats-value connection-name')
                                    .attr('x', 45)
                                    .attr('y', 14)
                                    .attr('width', 142);
                            } else {
                                backgrounds.push(connectionName.select('rect.connection-label-background'));
                                borders.push(connectionName.select('rect.connection-label-border'));
                            }

                            // update the connection name positioning
                            connectionName.attr('transform', function () {
                                const y: number = rowHeight * labelCount++;
                                return 'translate(0, ' + y + ')';
                            });

                            // update the connection name
                            connectionName
                                .select('text.connection-name')
                                .each(function (this: any) {
                                    const 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
                                    self.canvasUtils.ellipsis(
                                        connectionToLabel,
                                        connectionNameValue,
                                        'connection-name'
                                    );
                                })
                                .append('title')
                                .text(function () {
                                    return connectionNameValue;
                                });
                        } else {
                            // there is no connection name, remove the previous if necessary
                            connectionName.remove();
                        }
                    } else {
                        // no permissions to read to remove previous if necessary
                        connectionFrom.remove();
                        connectionTo.remove();
                        connectionName.remove();
                    }

                    // -------------------------
                    // connection label - queued
                    // -------------------------

                    // see if the queue label is already rendered
                    let queued: any = connectionLabelContainer.select('g.queued-container');
                    if (queued.empty()) {
                        queued = connectionLabelContainer.append('g').attr('class', 'queued-container');

                        // background
                        backgrounds.push(
                            queued
                                .append('rect')
                                .attr('class', 'connection-label-background')
                                .attr('width', ConnectionManager.DIMENSIONS.width)
                                .attr('height', rowHeight + ConnectionManager.HEIGHT_FOR_BACKPRESSURE)
                        );

                        // border
                        borders.push(
                            queued
                                .append('rect')
                                .attr('class', 'connection-label-border')
                                .attr('width', ConnectionManager.DIMENSIONS.width)
                                .attr('height', 1)
                        );

                        queued.append('text').attr('class', 'stats-label').attr('x', 5).attr('y', 14).text('Queued');

                        const queuedText = queued
                            .append('text')
                            .attr('class', 'stats-value queued')
                            .attr('x', 55)
                            .attr('y', 14);

                        // queued count
                        queuedText.append('tspan').attr('class', 'count');

                        // queued size
                        queuedText.append('tspan').attr('class', 'size');

                        // load balance icon
                        // x is set dynamically to slide to right, depending on whether expiration icon is shown.
                        queued
                            .append('text')
                            .attr('class', 'load-balance-icon')
                            .attr('y', 14)
                            .text(function () {
                                return '\uf042';
                            })
                            .append('title');

                        // expiration icon
                        queued
                            .append('text')
                            .attr('class', 'expiration-icon primary-color')
                            .attr('x', 208)
                            .attr('y', 14)
                            .text(function () {
                                return '\uf017';
                            })
                            .append('title');

                        const yBackpressureOffset: number = rowHeight + ConnectionManager.HEIGHT_FOR_BACKPRESSURE - 4;

                        // backpressure object threshold
                        const backpressureObjectContainer = queued
                            .append('g')
                            .attr(
                                'transform',
                                'translate(' +
                                    ConnectionManager.BACKPRESSURE_COUNT_OFFSET +
                                    ', ' +
                                    yBackpressureOffset +
                                    ')'
                            )
                            .attr('class', 'backpressure-object-container');

                        // start
                        backpressureObjectContainer
                            .append('rect')
                            .attr('class', 'backpressure-tick object')
                            .attr('width', 1)
                            .attr('height', 3)
                            .attr('x', 0)
                            .attr('y', 0);

                        // bar
                        backpressureObjectContainer
                            .append('rect')
                            .attr('class', 'backpressure-object')
                            .attr('width', ConnectionManager.BACKPRESSURE_BAR_WIDTH)
                            .attr('height', 3)
                            .attr('x', 0)
                            .attr('y', 0);

                        // end
                        backpressureObjectContainer
                            .append('rect')
                            .attr('class', 'backpressure-tick object')
                            .attr('width', 1)
                            .attr('height', 3)
                            .attr('x', ConnectionManager.BACKPRESSURE_BAR_WIDTH)
                            .attr('y', 0);

                        // percent full
                        backpressureObjectContainer
                            .append('rect')
                            .attr('class', 'backpressure-percent object')
                            .attr('width', 0)
                            .attr('height', 3)
                            .attr('x', 0)
                            .attr('y', 0);

                        // prediction indicator
                        backpressureObjectContainer
                            .append('rect')
                            .attr('class', 'backpressure-tick object-prediction')
                            .attr('width', 1)
                            .attr('height', 3)
                            .attr('x', ConnectionManager.BACKPRESSURE_BAR_WIDTH)
                            .attr('y', 0);

                        // backpressure data size threshold

                        const backpressureDataSizeContainer = queued
                            .append('g')
                            .attr(
                                'transform',
                                'translate(' +
                                    ConnectionManager.BACKPRESSURE_DATASIZE_OFFSET +
                                    ', ' +
                                    yBackpressureOffset +
                                    ')'
                            )
                            .attr('class', 'backpressure-data-size-container');

                        // start
                        backpressureDataSizeContainer
                            .append('rect')
                            .attr('class', 'backpressure-tick data-size')
                            .attr('width', 1)
                            .attr('height', 3)
                            .attr('x', 0)
                            .attr('y', 0);

                        // bar
                        backpressureDataSizeContainer
                            .append('rect')
                            .attr('class', 'backpressure-data-size')
                            .attr('width', ConnectionManager.BACKPRESSURE_BAR_WIDTH)
                            .attr('height', 3)
                            .attr('x', 0)
                            .attr('y', 0)
                            .append('title');

                        // end
                        backpressureDataSizeContainer
                            .append('rect')
                            .attr('class', 'backpressure-tick data-size')
                            .attr('width', 1)
                            .attr('height', 3)
                            .attr('x', ConnectionManager.BACKPRESSURE_BAR_WIDTH)
                            .attr('y', 0);

                        // percent full
                        backpressureDataSizeContainer
                            .append('rect')
                            .attr('class', 'backpressure-percent data-size')
                            .attr('width', 0)
                            .attr('height', 3)
                            .attr('x', 0)
                            .attr('y', 0);

                        // prediction indicator
                        backpressureDataSizeContainer
                            .append('rect')
                            .attr('class', 'backpressure-tick data-size-prediction')
                            .attr('width', 1)
                            .attr('height', 3)
                            .attr('x', ConnectionManager.BACKPRESSURE_BAR_WIDTH)
                            .attr('y', 0);
                    } else {
                        backgrounds.push(queued.select('rect.connection-label-background'));
                        borders.push(queued.select('rect.connection-label-border'));
                    }

                    // update the queued vertical positioning as necessary
                    queued.attr('transform', function () {
                        const y: number = rowHeight * labelCount++;
                        return 'translate(0, ' + y + ')';
                    });

                    // update the height based on the labels being rendered
                    connectionLabelContainer
                        .select('rect.body')
                        .attr('height', function () {
                            return rowHeight * labelCount + ConnectionManager.HEIGHT_FOR_BACKPRESSURE;
                        })
                        .classed('unauthorized', function () {
                            return d.permissions.canRead === false;
                        });
                    connectionLabelContainer
                        .select('rect.border')
                        .attr('height', function () {
                            return rowHeight * labelCount + ConnectionManager.HEIGHT_FOR_BACKPRESSURE;
                        })
                        .classed('unauthorized', function () {
                            return d.permissions.canRead === false;
                        });

                    // update the coloring of the backgrounds
                    backgrounds.forEach((background, i) => {
                        if (i % 2 === 0) {
                            background.attr('class', 'odd');
                        } else {
                            background.attr('class', 'even');
                        }
                    });

                    // update the coloring of the label borders
                    borders.forEach((border, i) => {
                        if (i > 0) {
                            border.attr('class', 'row-border');
                        } else {
                            border.attr('class', 'transparent');
                        }
                    });

                    // determine whether or not to show the load-balance icon
                    connectionLabelContainer
                        .select('text.load-balance-icon')
                        .classed('hidden', function () {
                            if (d.permissions.canRead) {
                                return !self.isLoadBalanceConfigured(d.component);
                            } else {
                                return true;
                            }
                        })
                        .classed('load-balance-icon-active fa-rotate-90 success-color-variant', function (d: any) {
                            return d.permissions.canRead && d.component.loadBalanceStatus === 'LOAD_BALANCE_ACTIVE';
                        })
                        .classed('primary-color', function (d: any) {
                            return d.permissions.canRead && d.component.loadBalanceStatus !== 'LOAD_BALANCE_ACTIVE';
                        })
                        .classed('load-balance-icon-184', function () {
                            return d.permissions.canRead && self.isExpirationConfigured(d.component);
                        })
                        .classed('load-balance-icon-200', function () {
                            return d.permissions.canRead && !self.isExpirationConfigured(d.component);
                        })
                        .attr('x', function () {
                            return d.permissions.canRead && self.isExpirationConfigured(d.component) ? 192 : 208;
                        })
                        .select('title')
                        .text(function () {
                            if (d.permissions.canRead) {
                                let loadBalanceStrategyText = '';

                                const loadBalanceStrategyOption: SelectOption | undefined = loadBalanceStrategies.find(
                                    (option) => option.value == d.component.loadBalanceStrategy
                                );
                                if (loadBalanceStrategyOption) {
                                    loadBalanceStrategyText = loadBalanceStrategyOption.text;
                                }

                                if ('PARTITION_BY_ATTRIBUTE' === d.component.loadBalanceStrategy) {
                                    loadBalanceStrategyText += ' (' + d.component.loadBalancePartitionAttribute + ')';
                                }

                                let loadBalanceCompression = 'no compression';
                                switch (d.component.loadBalanceCompression) {
                                    case 'COMPRESS_ATTRIBUTES_ONLY':
                                        loadBalanceCompression = "'Attribute' compression";
                                        break;
                                    case 'COMPRESS_ATTRIBUTES_AND_CONTENT':
                                        loadBalanceCompression = "'Attribute and content' compression";
                                        break;
                                }

                                const loadBalanceStatus: string =
                                    'LOAD_BALANCE_ACTIVE' === d.component.loadBalanceStatus
                                        ? ' Actively balancing...'
                                        : '';
                                return (
                                    'Load Balance is configured' +
                                    " with '" +
                                    loadBalanceStrategyText +
                                    "' strategy" +
                                    ' and ' +
                                    loadBalanceCompression +
                                    '.' +
                                    loadBalanceStatus
                                );
                            } else {
                                return '';
                            }
                        });

                    // determine whether or not to show the expiration icon
                    connectionLabelContainer
                        .select('text.expiration-icon')
                        .classed('hidden', function () {
                            if (d.permissions.canRead) {
                                return !self.isExpirationConfigured(d.component);
                            } else {
                                return true;
                            }
                        })
                        .select('title')
                        .text(function () {
                            if (d.permissions.canRead) {
                                return 'Expires FlowFiles older than ' + d.component.flowFileExpiration;
                            } else {
                                return '';
                            }
                        });

                    // update backpressure object fill
                    connectionLabelContainer.select('rect.backpressure-object').classed('not-configured', function () {
                        return d.status.aggregateSnapshot.percentUseCount == null;
                    });
                    connectionLabelContainer
                        .selectAll('rect.backpressure-tick.object')
                        .classed('not-configured', function () {
                            return d.status.aggregateSnapshot.percentUseCount == null;
                        });
                    connectionLabelContainer
                        .selectAll('rect.backpressure-tick.object-prediction')
                        .classed('not-configured', function () {
                            return d.status.aggregateSnapshot.percentUseCount == null;
                        });

                    // update backpressure data size fill
                    connectionLabelContainer
                        .select('rect.backpressure-data-size')
                        .classed('not-configured', function () {
                            return d.status.aggregateSnapshot.percentUseBytes == null;
                        });
                    connectionLabelContainer
                        .selectAll('rect.backpressure-tick.data-size')
                        .classed('not-configured', function () {
                            return d.status.aggregateSnapshot.percentUseBytes == null;
                        });
                    connectionLabelContainer
                        .selectAll('rect.backpressure-tick.data-size-prediction')
                        .classed('not-configured', function () {
                            return d.status.aggregateSnapshot.percentUseBytes == null;
                        });

                    if (d.permissions.canWrite) {
                        // only support dragging the label when appropriate
                        connectionLabelContainer.call(self.labelDrag);
                    }

                    // update the connection status
                    self.updateConnectionStatus(connection);
                } else {
                    if (!connectionLabelContainer.empty()) {
                        connectionLabelContainer.remove();
                    }
                }
            }

            // update the position of the label if possible
            self.transitionBehavior
                .transition(connection.select('g.connection-label-container'), self.transitionRequired)
                .attr('transform', function (this: any) {
                    const label: any = d3.select(this).select('rect.body');
                    const position: Position = self.getLabelPosition(label);
                    return 'translate(' + position.x + ', ' + position.y + ')';
                });
        });
    }