mirror of https://github.com/apache/nifi.git
NIFI-13044: Adding support to move to front (#8646)
* NIFI-13044: - Adding support to move to front. - Fixing revisions sent in various payloads. * NIFI-13047: - Address review feedback. This closes #8646
This commit is contained in:
parent
2d9943e2d3
commit
a79b210d4e
|
@ -52,7 +52,8 @@ import {
|
||||||
stopComponents,
|
stopComponents,
|
||||||
stopCurrentProcessGroup,
|
stopCurrentProcessGroup,
|
||||||
stopVersionControlRequest,
|
stopVersionControlRequest,
|
||||||
downloadFlow
|
downloadFlow,
|
||||||
|
moveToFront
|
||||||
} from '../state/flow/flow.actions';
|
} from '../state/flow/flow.actions';
|
||||||
import { ComponentType } from '../../../state/shared';
|
import { ComponentType } from '../../../state/shared';
|
||||||
import {
|
import {
|
||||||
|
@ -73,6 +74,7 @@ import { promptEmptyQueueRequest, promptEmptyQueuesRequest } from '../state/queu
|
||||||
import { getComponentStateAndOpenDialog } from '../../../state/component-state/component-state.actions';
|
import { getComponentStateAndOpenDialog } from '../../../state/component-state/component-state.actions';
|
||||||
import { navigateToComponentDocumentation } from '../../../state/documentation/documentation.actions';
|
import { navigateToComponentDocumentation } from '../../../state/documentation/documentation.actions';
|
||||||
import * as d3 from 'd3';
|
import * as d3 from 'd3';
|
||||||
|
import { Client } from '../../../service/client.service';
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
|
@ -557,7 +559,7 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
id: d.id,
|
id: d.id,
|
||||||
uri: d.uri,
|
uri: d.uri,
|
||||||
type: d.type,
|
type: d.type,
|
||||||
revision: d.revision
|
revision: this.client.getRevision(d)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
|
@ -597,7 +599,7 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
id: d.id,
|
id: d.id,
|
||||||
uri: d.uri,
|
uri: d.uri,
|
||||||
type: d.type,
|
type: d.type,
|
||||||
revision: d.revision
|
revision: this.client.getRevision(d)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
|
@ -625,7 +627,7 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
runOnce({
|
runOnce({
|
||||||
request: {
|
request: {
|
||||||
uri: d.uri,
|
uri: d.uri,
|
||||||
revision: d.revision
|
revision: this.client.getRevision(d)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
@ -678,7 +680,7 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
id: d.id,
|
id: d.id,
|
||||||
uri: d.uri,
|
uri: d.uri,
|
||||||
type: d.type,
|
type: d.type,
|
||||||
revision: d.revision
|
revision: this.client.getRevision(d)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -706,7 +708,7 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
id: d.id,
|
id: d.id,
|
||||||
uri: d.uri,
|
uri: d.uri,
|
||||||
type: d.type,
|
type: d.type,
|
||||||
revision: d.revision
|
revision: this.client.getRevision(d)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this.store.dispatch(
|
this.store.dispatch(
|
||||||
|
@ -982,12 +984,12 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
isSeparator: true
|
isSeparator: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
condition: (selection: any) => {
|
condition: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
return this.canvasUtils.isConnection(selection);
|
return this.canvasUtils.isConnection(selection);
|
||||||
},
|
},
|
||||||
clazz: 'fa fa-long-arrow-left',
|
clazz: 'fa fa-long-arrow-left',
|
||||||
text: 'Go to source',
|
text: 'Go to source',
|
||||||
action: (selection: any) => {
|
action: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
const selectionData = selection.datum();
|
const selectionData = selection.datum();
|
||||||
const remoteConnectableType: string = this.canvasUtils.getConnectableTypeForSource(
|
const remoteConnectableType: string = this.canvasUtils.getConnectableTypeForSource(
|
||||||
ComponentType.RemoteProcessGroup
|
ComponentType.RemoteProcessGroup
|
||||||
|
@ -1023,12 +1025,12 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
condition: (selection: any) => {
|
condition: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
return this.canvasUtils.isConnection(selection);
|
return this.canvasUtils.isConnection(selection);
|
||||||
},
|
},
|
||||||
clazz: 'fa fa-long-arrow-right',
|
clazz: 'fa fa-long-arrow-right',
|
||||||
text: 'Go to destination',
|
text: 'Go to destination',
|
||||||
action: (selection: any) => {
|
action: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
const selectionData = selection.datum();
|
const selectionData = selection.datum();
|
||||||
const remoteConnectableType: string = this.canvasUtils.getConnectableTypeForDestination(
|
const remoteConnectableType: string = this.canvasUtils.getConnectableTypeForDestination(
|
||||||
ComponentType.RemoteProcessGroup
|
ComponentType.RemoteProcessGroup
|
||||||
|
@ -1072,18 +1074,29 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
subMenuId: this.ALIGN.id
|
subMenuId: this.ALIGN.id
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
condition: (selection: any) => {
|
condition: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
// TODO - canMoveToFront
|
return this.canvasUtils.canMoveToFront(selection);
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
clazz: 'fa fa-clone',
|
clazz: 'fa fa-clone',
|
||||||
text: 'Bring to front',
|
text: 'Bring to front',
|
||||||
action: () => {
|
action: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
// TODO - toFront
|
const selectionData = selection.datum();
|
||||||
|
|
||||||
|
this.store.dispatch(
|
||||||
|
moveToFront({
|
||||||
|
request: {
|
||||||
|
componentType: selectionData.type,
|
||||||
|
id: selectionData.id,
|
||||||
|
uri: selectionData.uri,
|
||||||
|
revision: this.client.getRevision(selectionData),
|
||||||
|
zIndex: selectionData.zIndex
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
condition: (selection: any) => {
|
condition: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
return !selection.empty();
|
return !selection.empty();
|
||||||
},
|
},
|
||||||
clazz: 'fa fa-crosshairs',
|
clazz: 'fa fa-crosshairs',
|
||||||
|
@ -1104,12 +1117,12 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
condition: (selection: any) => {
|
condition: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
return this.canvasUtils.canRead(selection) && this.canvasUtils.isRemoteProcessGroup(selection);
|
return this.canvasUtils.canRead(selection) && this.canvasUtils.isRemoteProcessGroup(selection);
|
||||||
},
|
},
|
||||||
clazz: 'fa fa-external-link',
|
clazz: 'fa fa-external-link',
|
||||||
text: 'Go to',
|
text: 'Go to',
|
||||||
action: (selection: any) => {
|
action: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
const selectionData = selection.datum();
|
const selectionData = selection.datum();
|
||||||
const uri = selectionData.component.targetUri;
|
const uri = selectionData.component.targetUri;
|
||||||
|
|
||||||
|
@ -1120,12 +1133,12 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
isSeparator: true
|
isSeparator: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
condition: (selection: any) => {
|
condition: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
return this.canvasUtils.isNotRootGroup();
|
return this.canvasUtils.isNotRootGroup();
|
||||||
},
|
},
|
||||||
clazz: 'fa fa-arrows',
|
clazz: 'fa fa-arrows',
|
||||||
text: 'Move to parent group',
|
text: 'Move to parent group',
|
||||||
action: (selection: any) => {
|
action: (selection: d3.Selection<any, any, any, any>) => {
|
||||||
const components: MoveComponentRequest[] = [];
|
const components: MoveComponentRequest[] = [];
|
||||||
selection.each(function (d: any) {
|
selection.each(function (d: any) {
|
||||||
components.push({
|
components.push({
|
||||||
|
@ -1300,7 +1313,8 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private store: Store<CanvasState>,
|
private store: Store<CanvasState>,
|
||||||
private canvasUtils: CanvasUtils
|
private canvasUtils: CanvasUtils,
|
||||||
|
private client: Client
|
||||||
) {
|
) {
|
||||||
this.allMenus = new Map<string, ContextMenuDefinition>();
|
this.allMenus = new Map<string, ContextMenuDefinition>();
|
||||||
this.allMenus.set(this.ROOT_MENU.id, this.ROOT_MENU);
|
this.allMenus.set(this.ROOT_MENU.id, this.ROOT_MENU);
|
||||||
|
|
|
@ -1711,4 +1711,16 @@ export class CanvasUtils {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public canMoveToFront(selection: d3.Selection<any, any, any, any>): boolean {
|
||||||
|
// ensure the correct number of components are selected
|
||||||
|
if (selection.size() !== 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.canModify(selection) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.isConnection(selection) || this.isLabel(selection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,8 +270,8 @@ export class ConnectionManager {
|
||||||
* @param {type} connections
|
* @param {type} connections
|
||||||
*/
|
*/
|
||||||
private sort(connections: any[]): void {
|
private sort(connections: any[]): void {
|
||||||
connections.sort(function (a, b) {
|
connections.sort((a, b) => {
|
||||||
return a.zIndex === b.zIndex ? 0 : a.zIndex > b.zIndex ? 1 : -1;
|
return this.nifiCommon.compareNumber(a.zIndex, b.zIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,17 @@ export class LabelManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the specified labels according to the z index.
|
||||||
|
*
|
||||||
|
* @param {type} labels
|
||||||
|
*/
|
||||||
|
private sort(labels: any[]): void {
|
||||||
|
labels.sort((a, b) => {
|
||||||
|
return this.nifiCommon.compareNumber(a.zIndex, b.zIndex);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private renderLabels(entered: any) {
|
private renderLabels(entered: any) {
|
||||||
if (entered.empty()) {
|
if (entered.empty()) {
|
||||||
return entered;
|
return entered;
|
||||||
|
@ -393,6 +404,7 @@ export class LabelManager {
|
||||||
// update
|
// update
|
||||||
const updated = selection.merge(entered);
|
const updated = selection.merge(entered);
|
||||||
this.updateLabels(updated);
|
this.updateLabels(updated);
|
||||||
|
this.sort(updated);
|
||||||
|
|
||||||
// position
|
// position
|
||||||
this.positionBehavior.position(updated, this.transitionRequired);
|
this.positionBehavior.position(updated, this.transitionRequired);
|
||||||
|
|
|
@ -54,6 +54,7 @@ import {
|
||||||
LoadRemoteProcessGroupSuccess,
|
LoadRemoteProcessGroupSuccess,
|
||||||
LocalChangesDialogRequest,
|
LocalChangesDialogRequest,
|
||||||
MoveComponentsRequest,
|
MoveComponentsRequest,
|
||||||
|
MoveToFrontRequest,
|
||||||
NavigateToComponentRequest,
|
NavigateToComponentRequest,
|
||||||
NavigateToControllerServicesRequest,
|
NavigateToControllerServicesRequest,
|
||||||
NavigateToManageComponentPoliciesRequest,
|
NavigateToManageComponentPoliciesRequest,
|
||||||
|
@ -766,3 +767,5 @@ export const downloadFlow = createAction(
|
||||||
`${CANVAS_PREFIX} Download Flow Request`,
|
`${CANVAS_PREFIX} Download Flow Request`,
|
||||||
props<{ request: DownloadFlowRequest }>()
|
props<{ request: DownloadFlowRequest }>()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const moveToFront = createAction(`${CANVAS_PREFIX} Move To Front`, props<{ request: MoveToFrontRequest }>());
|
||||||
|
|
|
@ -51,6 +51,7 @@ import {
|
||||||
StopVersionControlRequest,
|
StopVersionControlRequest,
|
||||||
StopVersionControlResponse,
|
StopVersionControlResponse,
|
||||||
UpdateComponentFailure,
|
UpdateComponentFailure,
|
||||||
|
UpdateComponentRequest,
|
||||||
UpdateComponentResponse,
|
UpdateComponentResponse,
|
||||||
UpdateConnectionSuccess,
|
UpdateConnectionSuccess,
|
||||||
UpdateProcessorRequest,
|
UpdateProcessorRequest,
|
||||||
|
@ -62,6 +63,7 @@ import {
|
||||||
selectChangeVersionRequest,
|
selectChangeVersionRequest,
|
||||||
selectCurrentParameterContext,
|
selectCurrentParameterContext,
|
||||||
selectCurrentProcessGroupId,
|
selectCurrentProcessGroupId,
|
||||||
|
selectMaxZIndex,
|
||||||
selectParentProcessGroupId,
|
selectParentProcessGroupId,
|
||||||
selectProcessGroup,
|
selectProcessGroup,
|
||||||
selectProcessor,
|
selectProcessor,
|
||||||
|
@ -113,6 +115,7 @@ import { SaveVersionDialog } from '../../ui/canvas/items/flow/save-version-dialo
|
||||||
import { ChangeVersionDialog } from '../../ui/canvas/items/flow/change-version-dialog/change-version-dialog';
|
import { ChangeVersionDialog } from '../../ui/canvas/items/flow/change-version-dialog/change-version-dialog';
|
||||||
import { ChangeVersionProgressDialog } from '../../ui/canvas/items/flow/change-version-progress-dialog/change-version-progress-dialog';
|
import { ChangeVersionProgressDialog } from '../../ui/canvas/items/flow/change-version-progress-dialog/change-version-progress-dialog';
|
||||||
import { LocalChangesDialog } from '../../ui/canvas/items/flow/local-changes-dialog/local-changes-dialog';
|
import { LocalChangesDialog } from '../../ui/canvas/items/flow/local-changes-dialog/local-changes-dialog';
|
||||||
|
import { ClusterConnectionService } from '../../../../service/cluster-connection.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FlowEffects {
|
export class FlowEffects {
|
||||||
|
@ -127,6 +130,7 @@ export class FlowEffects {
|
||||||
private canvasView: CanvasView,
|
private canvasView: CanvasView,
|
||||||
private birdseyeView: BirdseyeView,
|
private birdseyeView: BirdseyeView,
|
||||||
private connectionManager: ConnectionManager,
|
private connectionManager: ConnectionManager,
|
||||||
|
private clusterConnectionService: ClusterConnectionService,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private dialog: MatDialog,
|
private dialog: MatDialog,
|
||||||
private propertyTableHelperService: PropertyTableHelperService,
|
private propertyTableHelperService: PropertyTableHelperService,
|
||||||
|
@ -2846,7 +2850,7 @@ export class FlowEffects {
|
||||||
version: selectedVersion.version
|
version: selectedVersion.version
|
||||||
},
|
},
|
||||||
processGroupRevision: request.revision,
|
processGroupRevision: request.revision,
|
||||||
disconnectedNodeAcknowledged: false
|
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged()
|
||||||
};
|
};
|
||||||
dialogRef.close();
|
dialogRef.close();
|
||||||
|
|
||||||
|
@ -3153,4 +3157,42 @@ export class FlowEffects {
|
||||||
),
|
),
|
||||||
{ dispatch: false }
|
{ dispatch: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
moveToFront$ = createEffect(() =>
|
||||||
|
this.actions$.pipe(
|
||||||
|
ofType(FlowActions.moveToFront),
|
||||||
|
map((action) => action.request),
|
||||||
|
concatLatestFrom((request) => this.store.select(selectMaxZIndex(request.componentType))),
|
||||||
|
filter(([request, maxZIndex]) => request.zIndex < maxZIndex),
|
||||||
|
switchMap(([request, maxZIndex]) => {
|
||||||
|
const updateRequest: UpdateComponentRequest = {
|
||||||
|
id: request.id,
|
||||||
|
type: request.componentType,
|
||||||
|
uri: request.uri,
|
||||||
|
payload: {
|
||||||
|
revision: request.revision,
|
||||||
|
disconnectedNodeAcknowledged: this.clusterConnectionService.isDisconnectionAcknowledged(),
|
||||||
|
component: {
|
||||||
|
id: request.id,
|
||||||
|
zIndex: maxZIndex + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return from(this.flowService.updateComponent(updateRequest)).pipe(
|
||||||
|
map((response) => {
|
||||||
|
const updateResponse: UpdateComponentResponse = {
|
||||||
|
id: updateRequest.id,
|
||||||
|
type: updateRequest.type,
|
||||||
|
response
|
||||||
|
};
|
||||||
|
return FlowActions.updateComponentSuccess({ response: updateResponse });
|
||||||
|
}),
|
||||||
|
catchError((errorResponse: HttpErrorResponse) =>
|
||||||
|
of(FlowActions.flowSnackbarError({ error: errorResponse.error }))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { flowFeatureKey, FlowState, SelectedComponent } from './index';
|
||||||
import { createSelector } from '@ngrx/store';
|
import { createSelector } from '@ngrx/store';
|
||||||
import { CanvasState, selectCanvasState } from '../index';
|
import { CanvasState, selectCanvasState } from '../index';
|
||||||
import { selectCurrentRoute } from '../../../../state/router/router.selectors';
|
import { selectCurrentRoute } from '../../../../state/router/router.selectors';
|
||||||
|
import { ComponentType } from '../../../../state/shared';
|
||||||
|
|
||||||
export const selectFlowState = createSelector(selectCanvasState, (state: CanvasState) => state[flowFeatureKey]);
|
export const selectFlowState = createSelector(selectCanvasState, (state: CanvasState) => state[flowFeatureKey]);
|
||||||
|
|
||||||
|
@ -246,3 +247,15 @@ export const selectNavigationCollapsed = createSelector(
|
||||||
);
|
);
|
||||||
|
|
||||||
export const selectOperationCollapsed = createSelector(selectFlowState, (state: FlowState) => state.operationCollapsed);
|
export const selectOperationCollapsed = createSelector(selectFlowState, (state: FlowState) => state.operationCollapsed);
|
||||||
|
|
||||||
|
export const selectMaxZIndex = (componentType: ComponentType.Connection | ComponentType.Label) => {
|
||||||
|
if (componentType === ComponentType.Connection) {
|
||||||
|
return createSelector(selectConnections, (connections: any[]) =>
|
||||||
|
connections.reduce((maxZIndex, connection) => Math.max(maxZIndex, connection.zIndex), -1)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return createSelector(selectLabels, (labels: any[]) =>
|
||||||
|
labels.reduce((maxZIndex, label) => Math.max(maxZIndex, label.zIndex), -1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -739,3 +739,11 @@ export interface DownloadFlowRequest {
|
||||||
processGroupId: string;
|
processGroupId: string;
|
||||||
includeReferencedServices: boolean;
|
includeReferencedServices: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MoveToFrontRequest {
|
||||||
|
componentType: ComponentType.Connection | ComponentType.Label;
|
||||||
|
id: string;
|
||||||
|
uri: string;
|
||||||
|
revision: Revision;
|
||||||
|
zIndex: number;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue