mirror of https://github.com/apache/nifi.git
NIFI-13062: Read only configuration dialogs (#8665)
* NIFI-13062: - Updating Configuration dialogs to support a read only mode for Processors, Controller Services, Reporting Tasks, Ports, Connections, Process Groups, Remote Process Groups, Flow Analysis Rules, and Parameter Contexts. - For extensions points rendered the type in the header. * NIFI-13062: - Addressing review feedback. This closes #8665
This commit is contained in:
parent
8c3c1eea31
commit
36c0ec4d7d
|
@ -37,7 +37,7 @@ export class QuickSelectBehavior {
|
|||
const selection: any = this.canvasUtils.getSelection();
|
||||
const selectionData: any = selection.datum();
|
||||
|
||||
if (this.canvasUtils.isConfigurable(selection)) {
|
||||
if (this.canvasUtils.isConfigurable(selection) || this.canvasUtils.hasDetails(selection)) {
|
||||
// show configuration dialog
|
||||
this.store.dispatch(
|
||||
navigateToEditComponent({
|
||||
|
@ -47,9 +47,6 @@ export class QuickSelectBehavior {
|
|||
}
|
||||
})
|
||||
);
|
||||
} else if (this.canvasUtils.hasDetails(selection)) {
|
||||
// TODO - show details (read only)... update Edit Forms to support readonly directive
|
||||
// nfActions.showDetails(selection);
|
||||
}
|
||||
|
||||
// stop propagation and prevent default
|
||||
|
|
|
@ -435,6 +435,28 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
condition: (selection: d3.Selection<any, any, any, any>) => {
|
||||
return this.canvasUtils.hasDetails(selection);
|
||||
},
|
||||
clazz: 'fa fa-gear',
|
||||
text: 'View configuration',
|
||||
action: (selection: d3.Selection<any, any, any, any>) => {
|
||||
if (selection.empty()) {
|
||||
this.store.dispatch(navigateToEditCurrentProcessGroup());
|
||||
} else {
|
||||
const selectionData = selection.datum();
|
||||
this.store.dispatch(
|
||||
navigateToEditComponent({
|
||||
request: {
|
||||
type: selectionData.type,
|
||||
id: selectionData.id
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
condition: (selection: any) => {
|
||||
if (this.canvasUtils.canRead(selection) && this.canvasUtils.isProcessor(selection)) {
|
||||
|
@ -481,17 +503,6 @@ export class CanvasContextMenu implements ContextMenuDefinitionProvider {
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
condition: (selection: any) => {
|
||||
// TODO - hasDetails
|
||||
return false;
|
||||
},
|
||||
clazz: 'fa fa-gear',
|
||||
text: 'View configuration',
|
||||
action: (selection: any) => {
|
||||
// TODO - showDetails... Can we support read only and configurable in the same dialog/form?
|
||||
}
|
||||
},
|
||||
{
|
||||
condition: (selection: any) => {
|
||||
// TODO - hasParameterContext
|
||||
|
|
|
@ -248,12 +248,35 @@ export class CanvasUtils {
|
|||
return selectionSize === readableSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the specified Processor, Input Port, or Output Port currently supports modification.
|
||||
*
|
||||
* @param entity
|
||||
*/
|
||||
public runnableSupportsModification(entity: any): boolean {
|
||||
return !(
|
||||
entity.status.aggregateSnapshot.runStatus === 'Running' ||
|
||||
entity.status.aggregateSnapshot.activeThreadCount > 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the specified Remote Process Group currently supports modification.
|
||||
*
|
||||
* @param entity
|
||||
*/
|
||||
public remoteProcessGroupSupportsModification(entity: any): boolean {
|
||||
return !(
|
||||
entity.status.transmissionStatus === 'Transmitting' || entity.status.aggregateSnapshot.activeThreadCount > 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the specified selection is in a state to support modification.
|
||||
*
|
||||
* @argument {selection} selection The selection
|
||||
*/
|
||||
public supportsModification(selection: any): boolean {
|
||||
private supportsModification(selection: d3.Selection<any, any, any, any>): boolean {
|
||||
if (selection.size() !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
@ -263,15 +286,9 @@ export class CanvasUtils {
|
|||
|
||||
let supportsModification = false;
|
||||
if (this.isProcessor(selection) || this.isInputPort(selection) || this.isOutputPort(selection)) {
|
||||
supportsModification = !(
|
||||
selectionData.status.aggregateSnapshot.runStatus === 'Running' ||
|
||||
selectionData.status.aggregateSnapshot.activeThreadCount > 0
|
||||
);
|
||||
supportsModification = this.runnableSupportsModification(selectionData);
|
||||
} else if (this.isRemoteProcessGroup(selection)) {
|
||||
supportsModification = !(
|
||||
selectionData.status.transmissionStatus === 'Transmitting' ||
|
||||
selectionData.status.aggregateSnapshot.activeThreadCount > 0
|
||||
);
|
||||
supportsModification = this.remoteProcessGroupSupportsModification(selectionData);
|
||||
} else if (this.isProcessGroup(selection)) {
|
||||
supportsModification = true;
|
||||
} else if (this.isFunnel(selection)) {
|
||||
|
@ -383,20 +400,26 @@ export class CanvasUtils {
|
|||
* @param selection
|
||||
*/
|
||||
public hasDetails(selection: any): boolean {
|
||||
if (selection.empty()) {
|
||||
return this.canvasPermissions.canRead && !this.canvasPermissions.canWrite;
|
||||
}
|
||||
|
||||
// ensure the correct number of components are selected
|
||||
if (selection.size() !== 1) {
|
||||
if (selection.size() > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.canRead(selection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.canModify(selection)) {
|
||||
if (
|
||||
this.isProcessor(selection) ||
|
||||
this.isInputPort(selection) ||
|
||||
this.isOutputPort(selection) ||
|
||||
this.isRemoteProcessGroup(selection) ||
|
||||
this.isProcessGroup(selection) ||
|
||||
this.isConnection(selection)
|
||||
) {
|
||||
return !this.isConfigurable(selection);
|
||||
|
@ -404,10 +427,11 @@ export class CanvasUtils {
|
|||
} else {
|
||||
return (
|
||||
this.isProcessor(selection) ||
|
||||
this.isConnection(selection) ||
|
||||
this.isInputPort(selection) ||
|
||||
this.isOutputPort(selection) ||
|
||||
this.isRemoteProcessGroup(selection)
|
||||
this.isRemoteProcessGroup(selection) ||
|
||||
this.isProcessGroup(selection) ||
|
||||
this.isConnection(selection)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1830,11 +1854,7 @@ export class CanvasUtils {
|
|||
|
||||
if (this.isProcessor(selection)) {
|
||||
const data = selection.datum();
|
||||
const supportsModification = !(
|
||||
data.status.aggregateSnapshot.runStatus === 'Running' ||
|
||||
data.status.aggregateSnapshot.activeThreadCount > 0
|
||||
);
|
||||
|
||||
const supportsModification = this.runnableSupportsModification(data);
|
||||
return supportsModification && data.component.multipleVersionsAvailable;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -68,7 +68,9 @@ import {
|
|||
selectCopiedSnippet,
|
||||
selectCurrentParameterContext,
|
||||
selectCurrentProcessGroupId,
|
||||
selectInputPort,
|
||||
selectMaxZIndex,
|
||||
selectOutputPort,
|
||||
selectParentProcessGroupId,
|
||||
selectProcessGroup,
|
||||
selectProcessor,
|
||||
|
@ -1294,6 +1296,12 @@ export class FlowEffects {
|
|||
editDialogReference.componentInstance.selectProcessor = (id: string) => {
|
||||
return this.store.select(selectProcessor(id));
|
||||
};
|
||||
editDialogReference.componentInstance.selectInputPort = (id: string) => {
|
||||
return this.store.select(selectInputPort(id));
|
||||
};
|
||||
editDialogReference.componentInstance.selectOutputPort = (id: string) => {
|
||||
return this.store.select(selectOutputPort(id));
|
||||
};
|
||||
editDialogReference.componentInstance.selectProcessGroup = (id: string) => {
|
||||
return this.store.select(selectProcessGroup(id));
|
||||
};
|
||||
|
|
|
@ -29,11 +29,12 @@
|
|||
} @else {
|
||||
<mat-form-field>
|
||||
<mat-label>To Input</mat-label>
|
||||
<mat-select [(ngModel)]="selectedInputPort" (selectionChange)="handleChanged()" [disabled]="isDisabled">
|
||||
<mat-select [(ngModel)]="selectedInputPort" (selectionChange)="handleChanged()">
|
||||
@for (item of inputPortItems; track item) {
|
||||
@if (item.description) {
|
||||
<mat-option
|
||||
[value]="item.value"
|
||||
[disabled]="isDisabled"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(item)"
|
||||
|
@ -41,7 +42,7 @@
|
|||
>{{ item.text }}</mat-option
|
||||
>
|
||||
} @else {
|
||||
<mat-option [value]="item.value">{{ item.text }}</mat-option>
|
||||
<mat-option [value]="item.value" [disabled]="isDisabled">{{ item.text }}</mat-option>
|
||||
}
|
||||
}
|
||||
</mat-select>
|
||||
|
|
|
@ -49,10 +49,12 @@ import { SelectOption, TextTipInput } from '../../../../../../../../state/shared
|
|||
})
|
||||
export class DestinationProcessGroup implements ControlValueAccessor {
|
||||
@Input() set processGroup(processGroup: any) {
|
||||
if (processGroup.permissions.canRead) {
|
||||
this.groupName = processGroup.component.name;
|
||||
} else {
|
||||
this.groupName = processGroup.id;
|
||||
if (processGroup) {
|
||||
if (processGroup.permissions.canRead) {
|
||||
this.groupName = processGroup.component.name;
|
||||
} else {
|
||||
this.groupName = processGroup.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@Input() set inputPorts(inputPorts: any[]) {
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
} @else {
|
||||
<mat-form-field>
|
||||
<mat-label>To Input</mat-label>
|
||||
<mat-select [(ngModel)]="selectedInputPort" (selectionChange)="handleChanged()" [disabled]="isDisabled">
|
||||
<mat-select [(ngModel)]="selectedInputPort" (selectionChange)="handleChanged()">
|
||||
@for (item of inputPortItems; track item) {
|
||||
@if (item.description) {
|
||||
<mat-option
|
||||
[value]="item.value"
|
||||
[disabled]="item.disabled == true"
|
||||
[disabled]="item.disabled == true || isDisabled"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(item)"
|
||||
|
@ -39,7 +39,9 @@
|
|||
>{{ item.text }}</mat-option
|
||||
>
|
||||
} @else {
|
||||
<mat-option [value]="item.value" [disabled]="item.disabled == true">{{ item.text }}</mat-option>
|
||||
<mat-option [value]="item.value" [disabled]="item.disabled == true || isDisabled">{{
|
||||
item.text
|
||||
}}</mat-option>
|
||||
}
|
||||
}
|
||||
</mat-select>
|
||||
|
|
|
@ -49,23 +49,25 @@ import { SelectOption, TextTipInput } from '../../../../../../../../state/shared
|
|||
})
|
||||
export class DestinationRemoteProcessGroup implements ControlValueAccessor {
|
||||
@Input() set remoteProcessGroup(remoteProcessGroup: any) {
|
||||
const rpg = remoteProcessGroup.component;
|
||||
const inputPorts: any[] = rpg.contents.inputPorts;
|
||||
if (remoteProcessGroup) {
|
||||
const rpg = remoteProcessGroup.component;
|
||||
const inputPorts: any[] = rpg.contents.inputPorts;
|
||||
|
||||
if (inputPorts) {
|
||||
this.noPorts = inputPorts.length == 0;
|
||||
if (inputPorts) {
|
||||
this.noPorts = inputPorts.length == 0;
|
||||
|
||||
this.inputPortItems = inputPorts.map((inputPort) => {
|
||||
return {
|
||||
value: inputPort.id,
|
||||
text: inputPort.name,
|
||||
description: inputPort.comments,
|
||||
disabled: inputPort.exists === false
|
||||
};
|
||||
});
|
||||
this.inputPortItems = inputPorts.map((inputPort) => {
|
||||
return {
|
||||
value: inputPort.id,
|
||||
text: inputPort.name,
|
||||
description: inputPort.comments,
|
||||
disabled: inputPort.exists === false
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
this.groupName = rpg.name;
|
||||
}
|
||||
|
||||
this.groupName = rpg.name;
|
||||
}
|
||||
|
||||
protected readonly TextTip = TextTip;
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Connection</h2>
|
||||
<h2 mat-dialog-title>
|
||||
{{ connectionReadonly || sourceReadonly || destinationReadonly ? 'Connection Details' : 'Edit Connection' }}
|
||||
</h2>
|
||||
<form class="edit-connection-form" [formGroup]="editConnectionForm">
|
||||
<mat-dialog-content>
|
||||
<mat-tab-group>
|
||||
|
@ -42,9 +44,11 @@
|
|||
formControlName="source"></source-remote-process-group>
|
||||
}
|
||||
@case (ComponentType.InputPort) {
|
||||
<source-input-port
|
||||
[groupName]="getCurrentGroupName(breadcrumbs)"
|
||||
[inputPortName]="source.name"></source-input-port>
|
||||
@if (sourceInputPort$ | async) {
|
||||
<source-input-port
|
||||
[groupName]="getCurrentGroupName(breadcrumbs)"
|
||||
[inputPortName]="source.name"></source-input-port>
|
||||
}
|
||||
}
|
||||
@case (ComponentType.Funnel) {
|
||||
<source-funnel [groupName]="getCurrentGroupName(breadcrumbs)"></source-funnel>
|
||||
|
@ -70,9 +74,11 @@
|
|||
formControlName="destination"></destination-remote-process-group>
|
||||
}
|
||||
@case (ComponentType.OutputPort) {
|
||||
<destination-output-port
|
||||
[groupName]="getCurrentGroupName(breadcrumbs)"
|
||||
[outputPortName]="destinationName"></destination-output-port>
|
||||
@if (destinationOutputPort$ | async) {
|
||||
<destination-output-port
|
||||
[groupName]="getCurrentGroupName(breadcrumbs)"
|
||||
[outputPortName]="destinationName"></destination-output-port>
|
||||
}
|
||||
}
|
||||
@case (ComponentType.Funnel) {
|
||||
<destination-funnel
|
||||
|
@ -89,7 +95,11 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="name"
|
||||
type="text"
|
||||
[readonly]="connectionReadonly || sourceReadonly || destinationReadonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="flex flex-col mb-5">
|
||||
|
@ -99,20 +109,32 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>FlowFile Expiration</mat-label>
|
||||
<input matInput formControlName="flowFileExpiration" type="text" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="flowFileExpiration"
|
||||
type="text"
|
||||
[readonly]="connectionReadonly || sourceReadonly || destinationReadonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="flex gap-x-2">
|
||||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Back Pressure Object Threshold</mat-label>
|
||||
<input matInput formControlName="backPressureObjectThreshold" type="number" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="backPressureObjectThreshold"
|
||||
type="number"
|
||||
[readonly]="connectionReadonly || sourceReadonly || destinationReadonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Size Threshold</mat-label>
|
||||
<input matInput formControlName="backPressureDataSizeThreshold" type="text" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="backPressureDataSizeThreshold"
|
||||
type="text"
|
||||
[readonly]="connectionReadonly || sourceReadonly || destinationReadonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -126,6 +148,7 @@
|
|||
@for (option of loadBalanceStrategies; track option) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="connectionReadonly || sourceReadonly || destinationReadonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(option)"
|
||||
|
@ -140,7 +163,11 @@
|
|||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Attribute Name</mat-label>
|
||||
<input matInput formControlName="partitionAttribute" type="text" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="partitionAttribute"
|
||||
type="text"
|
||||
[readonly]="connectionReadonly || sourceReadonly || destinationReadonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
}
|
||||
|
@ -153,6 +180,7 @@
|
|||
@for (option of loadBalanceCompressionStrategies; track option) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="connectionReadonly || sourceReadonly || destinationReadonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(option)"
|
||||
|
@ -176,15 +204,19 @@
|
|||
</mat-dialog-content>
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close="CANCELLED">Cancel</button>
|
||||
<button
|
||||
[disabled]="!editConnectionForm.dirty || editConnectionForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="editConnection()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (connectionReadonly || sourceReadonly || destinationReadonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close="CANCELLED">Cancel</button>
|
||||
<button
|
||||
[disabled]="!editConnectionForm.dirty || editConnectionForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="editConnection()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -49,7 +49,7 @@ import { SourceFunnel } from '../source/source-funnel/source-funnel.component';
|
|||
import { DestinationProcessor } from '../destination/destination-processor/destination-processor.component';
|
||||
import { DestinationOutputPort } from '../destination/destination-output-port/destination-output-port.component';
|
||||
import { SourceInputPort } from '../source/source-input-port/source-input-port.component';
|
||||
import { Observable } from 'rxjs';
|
||||
import { asyncScheduler, Observable, observeOn, tap } from 'rxjs';
|
||||
import { SourceProcessGroup } from '../source/source-process-group/source-process-group.component';
|
||||
import { DestinationProcessGroup } from '../destination/destination-process-group/destination-process-group.component';
|
||||
import { SourceRemoteProcessGroup } from '../source/source-remote-process-group/source-remote-process-group.component';
|
||||
|
@ -92,39 +92,96 @@ export class EditConnectionComponent {
|
|||
@Input() set getChildOutputPorts(getChildOutputPorts: (groupId: string) => Observable<any>) {
|
||||
if (this.sourceType == ComponentType.ProcessGroup) {
|
||||
this.childOutputPorts$ = getChildOutputPorts(this.source.groupId);
|
||||
this.sourceReadonly = false;
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
@Input() set getChildInputPorts(getChildInputPorts: (groupId: string) => Observable<any>) {
|
||||
if (this.destinationType == ComponentType.ProcessGroup) {
|
||||
this.childInputPorts$ = getChildInputPorts(this.destinationGroupId);
|
||||
this.destinationReadonly = false;
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
@Input() set selectProcessor(selectProcessor: (id: string) => Observable<any>) {
|
||||
if (this.sourceType == ComponentType.Processor) {
|
||||
this.sourceProcessor$ = selectProcessor(this.source.id);
|
||||
this.sourceProcessor$ = selectProcessor(this.source.id).pipe(
|
||||
observeOn(asyncScheduler),
|
||||
tap((processor) => {
|
||||
this.sourceReadonly = !this.canvasUtils.runnableSupportsModification(processor);
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
})
|
||||
);
|
||||
}
|
||||
if (this.destinationType == ComponentType.Processor && this.destinationId) {
|
||||
this.destinationProcessor$ = selectProcessor(this.destinationId);
|
||||
this.destinationProcessor$ = selectProcessor(this.destinationId).pipe(
|
||||
observeOn(asyncScheduler),
|
||||
tap((processor) => {
|
||||
this.destinationReadonly = !this.canvasUtils.runnableSupportsModification(processor);
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Input() set selectInputPort(selectInputPort: (id: string) => Observable<any>) {
|
||||
if (this.sourceType == ComponentType.InputPort) {
|
||||
this.sourceInputPort$ = selectInputPort(this.source.id).pipe(
|
||||
observeOn(asyncScheduler),
|
||||
tap((inputPort) => {
|
||||
this.sourceReadonly = !this.canvasUtils.runnableSupportsModification(inputPort);
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Input() set selectOutputPort(selectOutputPort: (id: string) => Observable<any>) {
|
||||
if (this.destinationType == ComponentType.OutputPort && this.destinationId) {
|
||||
this.destinationOutputPort$ = selectOutputPort(this.destinationId).pipe(
|
||||
observeOn(asyncScheduler),
|
||||
tap((outputPort) => {
|
||||
this.destinationReadonly = !this.canvasUtils.runnableSupportsModification(outputPort);
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Input() set selectProcessGroup(selectProcessGroup: (id: string) => Observable<any>) {
|
||||
if (this.sourceType == ComponentType.ProcessGroup) {
|
||||
this.sourceProcessGroup$ = selectProcessGroup(this.source.groupId);
|
||||
this.sourceReadonly = false;
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
}
|
||||
if (this.destinationType == ComponentType.ProcessGroup) {
|
||||
this.destinationProcessGroup$ = selectProcessGroup(this.destinationGroupId);
|
||||
this.destinationReadonly = false;
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
@Input() set selectRemoteProcessGroup(selectRemoteProcessGroup: (id: string) => Observable<any>) {
|
||||
if (this.sourceType == ComponentType.RemoteProcessGroup) {
|
||||
this.sourceRemoteProcessGroup$ = selectRemoteProcessGroup(this.source.groupId);
|
||||
this.sourceRemoteProcessGroup$ = selectRemoteProcessGroup(this.source.groupId).pipe(
|
||||
observeOn(asyncScheduler),
|
||||
tap((remoteProcessGroup) => {
|
||||
this.sourceReadonly = !this.canvasUtils.remoteProcessGroupSupportsModification(remoteProcessGroup);
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
})
|
||||
);
|
||||
}
|
||||
if (this.destinationType == ComponentType.RemoteProcessGroup) {
|
||||
this.destinationRemoteProcessGroup$ = selectRemoteProcessGroup(this.destinationGroupId);
|
||||
this.destinationRemoteProcessGroup$ = selectRemoteProcessGroup(this.destinationGroupId).pipe(
|
||||
observeOn(asyncScheduler),
|
||||
tap((remoteProcessGroup) => {
|
||||
this.destinationReadonly =
|
||||
!this.canvasUtils.remoteProcessGroupSupportsModification(remoteProcessGroup);
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,6 +193,9 @@ export class EditConnectionComponent {
|
|||
breadcrumbs$ = this.store.select(selectBreadcrumbs);
|
||||
|
||||
editConnectionForm: FormGroup;
|
||||
connectionReadonly: boolean;
|
||||
sourceReadonly: boolean = false;
|
||||
destinationReadonly: boolean = false;
|
||||
|
||||
source: any;
|
||||
sourceType: ComponentType | null;
|
||||
|
@ -147,14 +207,16 @@ export class EditConnectionComponent {
|
|||
destinationGroupId: string;
|
||||
destinationName: string;
|
||||
|
||||
sourceProcessor$!: Observable<any> | null;
|
||||
destinationProcessor$!: Observable<any> | null;
|
||||
sourceProcessGroup$!: Observable<any> | null;
|
||||
destinationProcessGroup$!: Observable<any> | null;
|
||||
sourceRemoteProcessGroup$!: Observable<any> | null;
|
||||
destinationRemoteProcessGroup$!: Observable<any> | null;
|
||||
childOutputPorts$!: Observable<any> | null;
|
||||
childInputPorts$!: Observable<any> | null;
|
||||
sourceProcessor$: Observable<any> | null = null;
|
||||
destinationProcessor$: Observable<any> | null = null;
|
||||
sourceInputPort$: Observable<any> | null = null;
|
||||
destinationOutputPort$: Observable<any> | null = null;
|
||||
sourceProcessGroup$: Observable<any> | null = null;
|
||||
destinationProcessGroup$: Observable<any> | null = null;
|
||||
sourceRemoteProcessGroup$: Observable<any> | null = null;
|
||||
destinationRemoteProcessGroup$: Observable<any> | null = null;
|
||||
childOutputPorts$: Observable<any> | null = null;
|
||||
childInputPorts$: Observable<any> | null = null;
|
||||
|
||||
loadBalancePartitionAttributeRequired = false;
|
||||
initialPartitionAttribute: string;
|
||||
|
@ -170,6 +232,8 @@ export class EditConnectionComponent {
|
|||
) {
|
||||
const connection: any = dialogRequest.entity.component;
|
||||
|
||||
this.connectionReadonly = !dialogRequest.entity.permissions.canWrite;
|
||||
|
||||
this.source = connection.source;
|
||||
this.sourceType = this.canvasUtils.getComponentTypeForSource(this.source.type);
|
||||
|
||||
|
@ -228,6 +292,44 @@ export class EditConnectionComponent {
|
|||
this.initialPartitionAttribute = connection.loadBalancePartitionAttribute;
|
||||
this.initialCompression = connection.loadBalanceCompression;
|
||||
this.loadBalanceChanged(connection.loadBalanceStrategy);
|
||||
|
||||
this.updateControlValueAccessorsForReadOnly();
|
||||
}
|
||||
|
||||
updateControlValueAccessorsForReadOnly(): void {
|
||||
const disabled = this.connectionReadonly || this.sourceReadonly || this.destinationReadonly;
|
||||
|
||||
// sourceReadonly is used to update the readonly / disable state of the form controls, note that
|
||||
// the source control for local and remote groups is always disabled (see above) in this edit
|
||||
// component because the source of the connection cannot be changed
|
||||
|
||||
if (disabled) {
|
||||
this.editConnectionForm.get('prioritizers')?.disable();
|
||||
|
||||
if (this.sourceType == ComponentType.Processor) {
|
||||
this.editConnectionForm.get('relationships')?.disable();
|
||||
}
|
||||
|
||||
if (
|
||||
this.destinationType == ComponentType.ProcessGroup ||
|
||||
this.destinationType == ComponentType.RemoteProcessGroup
|
||||
) {
|
||||
this.editConnectionForm.get('destination')?.disable();
|
||||
}
|
||||
} else {
|
||||
this.editConnectionForm.get('prioritizers')?.enable();
|
||||
|
||||
if (this.sourceType == ComponentType.Processor) {
|
||||
this.editConnectionForm.get('relationships')?.enable();
|
||||
}
|
||||
|
||||
if (
|
||||
this.destinationType == ComponentType.ProcessGroup ||
|
||||
this.destinationType == ComponentType.RemoteProcessGroup
|
||||
) {
|
||||
this.editConnectionForm.get('destination')?.enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentGroupName(breadcrumbs: BreadcrumbEntity): string {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.prioritizer-draggable-item {
|
||||
|
|
|
@ -29,11 +29,12 @@
|
|||
} @else {
|
||||
<mat-form-field>
|
||||
<mat-label>From Output</mat-label>
|
||||
<mat-select [(ngModel)]="selectedOutputPort" (selectionChange)="handleChanged()" [disabled]="isDisabled">
|
||||
<mat-select [(ngModel)]="selectedOutputPort" (selectionChange)="handleChanged()">
|
||||
@for (item of outputPortItems; track item) {
|
||||
@if (item.description) {
|
||||
<mat-option
|
||||
[value]="item.value"
|
||||
[disabled]="isDisabled"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(item)"
|
||||
|
@ -41,7 +42,7 @@
|
|||
>{{ item.text }}</mat-option
|
||||
>
|
||||
} @else {
|
||||
<mat-option [value]="item.value">{{ item.text }}</mat-option>
|
||||
<mat-option [value]="item.value" [disabled]="isDisabled">{{ item.text }}</mat-option>
|
||||
}
|
||||
}
|
||||
</mat-select>
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
} @else {
|
||||
<mat-form-field>
|
||||
<mat-label>From Output</mat-label>
|
||||
<mat-select [(ngModel)]="selectedOutputPort" (selectionChange)="handleChanged()" [disabled]="isDisabled">
|
||||
<mat-select [(ngModel)]="selectedOutputPort" (selectionChange)="handleChanged()">
|
||||
@for (item of outputPortItems; track item) {
|
||||
@if (item.description) {
|
||||
<mat-option
|
||||
[value]="item.value"
|
||||
[disabled]="item.disabled == true"
|
||||
[disabled]="item.disabled == true || isDisabled"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(item)"
|
||||
|
@ -39,7 +39,9 @@
|
|||
>{{ item.text }}</mat-option
|
||||
>
|
||||
} @else {
|
||||
<mat-option [value]="item.value" [disabled]="item.disabled == true">{{ item.text }}</mat-option>
|
||||
<mat-option [value]="item.value" [disabled]="item.disabled == true || isDisabled">{{
|
||||
item.text
|
||||
}}</mat-option>
|
||||
}
|
||||
}
|
||||
</mat-select>
|
||||
|
|
|
@ -15,21 +15,21 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit {{ portTypeLabel }}</h2>
|
||||
<h2 mat-dialog-title>{{ readonly ? portTypeLabel + ' Details' : 'Edit ' + portTypeLabel }}</h2>
|
||||
<form class="edit-port-form" [formGroup]="editPortForm">
|
||||
<error-banner></error-banner>
|
||||
<mat-dialog-content>
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>{{ portTypeLabel }} Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input matInput formControlName="name" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
@if (request.entity.component.allowRemoteAccess) {
|
||||
<mat-form-field>
|
||||
<mat-label>Concurrent Tasks</mat-label>
|
||||
<input matInput formControlName="concurrentTasks" type="text" />
|
||||
<input matInput formControlName="concurrentTasks" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
}
|
||||
</div>
|
||||
|
@ -42,21 +42,25 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments" type="text"></textarea>
|
||||
<textarea matInput formControlName="comments" type="text" [readonly]="readonly"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editPortForm.dirty || editPortForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="editPort()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editPortForm.dirty || editPortForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="editPort()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -32,6 +32,7 @@ import { AsyncPipe } from '@angular/common';
|
|||
import { selectSaving } from '../../../../../state/flow/flow.selectors';
|
||||
import { NifiSpinnerDirective } from '../../../../../../../ui/common/spinner/nifi-spinner.directive';
|
||||
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
|
||||
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-port',
|
||||
|
@ -53,15 +54,20 @@ export class EditPort {
|
|||
saving$ = this.store.select(selectSaving);
|
||||
|
||||
editPortForm: FormGroup;
|
||||
readonly: boolean;
|
||||
portTypeLabel: string;
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public request: EditComponentDialogRequest,
|
||||
private formBuilder: FormBuilder,
|
||||
private store: Store<CanvasState>,
|
||||
private canvasUtils: CanvasUtils,
|
||||
private client: Client,
|
||||
private clusterConnectionService: ClusterConnectionService
|
||||
) {
|
||||
this.readonly =
|
||||
!request.entity.permissions.canWrite || !this.canvasUtils.runnableSupportsModification(request.entity);
|
||||
|
||||
// set the port type name
|
||||
if (ComponentType.InputPort == this.request.type) {
|
||||
this.portTypeLabel = 'Input Port';
|
||||
|
@ -77,7 +83,10 @@ export class EditPort {
|
|||
Validators.required
|
||||
),
|
||||
comments: new FormControl(request.entity.component.comments),
|
||||
portFunction: new FormControl(request.entity.component.portFunction == 'FAILURE')
|
||||
portFunction: new FormControl({
|
||||
value: request.entity.component.portFunction == 'FAILURE',
|
||||
disabled: this.readonly
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -98,7 +107,7 @@ export class EditPort {
|
|||
}
|
||||
|
||||
// if this port is an output port update the port function
|
||||
if (ComponentType.OutputPort == this.request.entity.type) {
|
||||
if (ComponentType.OutputPort == this.request.type) {
|
||||
payload.component.portFunction = this.editPortForm.get('portFunction')?.value ? 'FAILURE' : 'STANDARD';
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Process Group</h2>
|
||||
<h2 mat-dialog-title>{{ readonly ? 'Process Group Details' : 'Edit Process Group' }}</h2>
|
||||
<form class="process-group-edit-form" [formGroup]="editProcessGroupForm">
|
||||
<mat-dialog-content>
|
||||
<mat-tab-group>
|
||||
|
@ -25,7 +25,7 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input matInput formControlName="name" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -36,6 +36,7 @@
|
|||
@if (option.description) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="readonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(option)"
|
||||
|
@ -43,7 +44,9 @@
|
|||
>{{ option.text }}</mat-option
|
||||
>
|
||||
} @else {
|
||||
<mat-option [value]="option.value">{{ option.text }}</mat-option>
|
||||
<mat-option [value]="option.value" [disabled]="readonly">{{
|
||||
option.text
|
||||
}}</mat-option>
|
||||
}
|
||||
}
|
||||
</mat-select>
|
||||
|
@ -64,6 +67,7 @@
|
|||
@for (option of executionEngineOptions; track option) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="readonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(option)"
|
||||
|
@ -81,6 +85,7 @@
|
|||
@for (option of flowfileConcurrencyOptions; track option) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="readonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(option)"
|
||||
|
@ -98,6 +103,7 @@
|
|||
@for (option of flowfileOutboundPolicyOptions; track option) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="readonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(option)"
|
||||
|
@ -113,25 +119,37 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Default FlowFile Expiration</mat-label>
|
||||
<input matInput formControlName="defaultFlowFileExpiration" type="text" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="defaultFlowFileExpiration"
|
||||
type="text"
|
||||
[readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Default Back Pressure Object Threshold</mat-label>
|
||||
<input matInput formControlName="defaultBackPressureObjectThreshold" type="text" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="defaultBackPressureObjectThreshold"
|
||||
type="text"
|
||||
[readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Default Back Pressure Data Size Threshold</mat-label>
|
||||
<input matInput formControlName="defaultBackPressureDataSizeThreshold" type="text" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="defaultBackPressureDataSizeThreshold"
|
||||
type="text"
|
||||
[readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Log File Suffix</mat-label>
|
||||
<input matInput formControlName="logFileSuffix" type="text" />
|
||||
<input matInput formControlName="logFileSuffix" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -141,7 +159,12 @@
|
|||
<div class="tab-content py-4">
|
||||
<mat-form-field>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments" type="text" rows="15"></textarea>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="comments"
|
||||
type="text"
|
||||
rows="15"
|
||||
[readonly]="readonly"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
@ -149,16 +172,20 @@
|
|||
</mat-dialog-content>
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editProcessGroupForm.dirty || editProcessGroupForm.invalid || saving.value"
|
||||
class="h-8"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editProcessGroupForm.dirty || editProcessGroupForm.invalid || saving.value"
|
||||
class="h-8"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -86,6 +86,7 @@ export class EditProcessGroup {
|
|||
protected readonly TextTip = TextTip;
|
||||
|
||||
editProcessGroupForm: FormGroup;
|
||||
readonly: boolean;
|
||||
parameterContextsOptions: SelectOption[] = [];
|
||||
|
||||
executionEngineOptions: SelectOption[] = [
|
||||
|
@ -155,6 +156,8 @@ export class EditProcessGroup {
|
|||
private client: Client,
|
||||
private clusterConnectionService: ClusterConnectionService
|
||||
) {
|
||||
this.readonly = !request.entity.permissions.canWrite;
|
||||
|
||||
this.parameterContextsOptions.push({
|
||||
text: 'No parameter context',
|
||||
value: null
|
||||
|
@ -162,7 +165,7 @@ export class EditProcessGroup {
|
|||
|
||||
this.editProcessGroupForm = this.formBuilder.group({
|
||||
name: new FormControl(request.entity.component.name, Validators.required),
|
||||
applyParameterContextRecursively: new FormControl(false),
|
||||
applyParameterContextRecursively: new FormControl({ value: false, disabled: this.readonly }),
|
||||
executionEngine: new FormControl(request.entity.component.executionEngine, Validators.required),
|
||||
flowfileConcurrency: new FormControl(request.entity.component.flowfileConcurrency, Validators.required),
|
||||
flowfileOutboundPolicy: new FormControl(
|
||||
|
|
|
@ -15,7 +15,16 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Processor</h2>
|
||||
<h2 mat-dialog-title>
|
||||
<div class="flex justify-between items-baseline">
|
||||
<div>
|
||||
{{ readonly ? 'Processor Details' : 'Edit Processor' }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ formatType(request.entity) }}
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<form class="processor-edit-form" [formGroup]="editProcessorForm">
|
||||
<error-banner></error-banner>
|
||||
<!-- TODO - Stop & Configure -->
|
||||
|
@ -27,7 +36,7 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input matInput formControlName="name" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="flex flex-col mb-5">
|
||||
|
@ -50,13 +59,17 @@
|
|||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Penalty Duration</mat-label>
|
||||
<input matInput formControlName="penaltyDuration" type="text" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="penaltyDuration"
|
||||
type="text"
|
||||
[readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Yield Duration</mat-label>
|
||||
<input matInput formControlName="yieldDuration" type="text" />
|
||||
<input matInput formControlName="yieldDuration" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -65,7 +78,7 @@
|
|||
<mat-label>Bulletin Level</mat-label>
|
||||
<mat-select formControlName="bulletinLevel">
|
||||
@for (option of bulletinLevels; track option) {
|
||||
<mat-option [value]="option.value">
|
||||
<mat-option [value]="option.value" [disabled]="readonly">
|
||||
{{ option.text }}
|
||||
</mat-option>
|
||||
}
|
||||
|
@ -88,6 +101,7 @@
|
|||
@for (option of schedulingStrategies; track option) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="readonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getSelectOptionTipData(option)"
|
||||
|
@ -107,7 +121,8 @@
|
|||
formControlName="concurrentTasks"
|
||||
(change)="concurrentTasksChanged()"
|
||||
name="concurrentTasks"
|
||||
type="number" />
|
||||
type="number"
|
||||
[readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="w-44">
|
||||
|
@ -117,7 +132,8 @@
|
|||
matInput
|
||||
formControlName="schedulingPeriod"
|
||||
(change)="schedulingPeriodChanged()"
|
||||
type="text" />
|
||||
type="text"
|
||||
[readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -127,7 +143,7 @@
|
|||
<mat-select formControlName="executionNode">
|
||||
@for (option of executionStrategies; track option) {
|
||||
<mat-option
|
||||
[disabled]="executionStrategyDisabled(option)"
|
||||
[disabled]="executionStrategyDisabled(option) || readonly"
|
||||
[value]="option.value"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
|
@ -189,7 +205,12 @@
|
|||
<div class="tab-content py-4">
|
||||
<mat-form-field>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments" type="text" rows="5"></textarea>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="comments"
|
||||
type="text"
|
||||
rows="5"
|
||||
[readonly]="readonly"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
@ -197,15 +218,19 @@
|
|||
</mat-dialog-content>
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editProcessorForm.dirty || editProcessorForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editProcessorForm.dirty || editProcessorForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
h2.mdc-dialog__title::before {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.processor-edit-form {
|
||||
@include mat.button-density(-1);
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ import {
|
|||
} from './relationship-settings/relationship-settings.component';
|
||||
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { ClusterConnectionService } from '../../../../../../../service/cluster-connection.service';
|
||||
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
|
||||
|
||||
@Component({
|
||||
selector: 'edit-processor',
|
||||
|
@ -87,6 +88,7 @@ export class EditProcessor {
|
|||
protected readonly TextTip = TextTip;
|
||||
|
||||
editProcessorForm: FormGroup;
|
||||
readonly: boolean;
|
||||
|
||||
bulletinLevels = [
|
||||
{
|
||||
|
@ -148,9 +150,13 @@ export class EditProcessor {
|
|||
@Inject(MAT_DIALOG_DATA) public request: EditComponentDialogRequest,
|
||||
private formBuilder: FormBuilder,
|
||||
private client: Client,
|
||||
private canvasUtils: CanvasUtils,
|
||||
private clusterConnectionService: ClusterConnectionService,
|
||||
private nifiCommon: NiFiCommon
|
||||
) {
|
||||
this.readonly =
|
||||
!request.entity.permissions.canWrite || !this.canvasUtils.runnableSupportsModification(request.entity);
|
||||
|
||||
const processorProperties: any = request.entity.component.config.properties;
|
||||
const properties: Property[] = Object.entries(processorProperties).map((entry: any) => {
|
||||
const [property, value] = entry;
|
||||
|
@ -205,15 +211,18 @@ export class EditProcessor {
|
|||
concurrentTasks: new FormControl(concurrentTasks, Validators.required),
|
||||
schedulingPeriod: new FormControl(schedulingPeriod, Validators.required),
|
||||
executionNode: new FormControl(request.entity.component.config.executionNode, Validators.required),
|
||||
properties: new FormControl(properties),
|
||||
relationshipConfiguration: new FormControl(relationshipConfiguration, Validators.required),
|
||||
properties: new FormControl({ value: properties, disabled: this.readonly }),
|
||||
relationshipConfiguration: new FormControl(
|
||||
{ value: relationshipConfiguration, disabled: this.readonly },
|
||||
Validators.required
|
||||
),
|
||||
comments: new FormControl(request.entity.component.config.comments)
|
||||
});
|
||||
|
||||
if (this.supportsBatching()) {
|
||||
this.editProcessorForm.addControl(
|
||||
'runDuration',
|
||||
new FormControl(this.runDurationMillis, Validators.required)
|
||||
new FormControl({ value: this.runDurationMillis, disabled: this.readonly }, Validators.required)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
type="number"
|
||||
name="retryCount"
|
||||
(keyup)="handleChanged()"
|
||||
[disabled]="isDisabled" />
|
||||
[readonly]="isDisabled" />
|
||||
<mat-icon
|
||||
matSuffix
|
||||
class="info-icon"
|
||||
|
@ -107,7 +107,7 @@
|
|||
[(ngModel)]="maxBackoffPeriod"
|
||||
type="text"
|
||||
(keyup)="handleChanged()"
|
||||
[disabled]="isDisabled" />
|
||||
[readonly]="isDisabled" />
|
||||
<mat-icon
|
||||
matSuffix
|
||||
class="info-icon"
|
||||
|
|
|
@ -16,20 +16,17 @@
|
|||
-->
|
||||
|
||||
<div class="run-duration-slider flex flex-col">
|
||||
<div class="pl-4 pr-7">
|
||||
<mat-slider
|
||||
[min]="0"
|
||||
[max]="7"
|
||||
[step]="1"
|
||||
showTickMarks
|
||||
discrete
|
||||
[displayWith]="formatLabel"
|
||||
[disabled]="isDisabled">
|
||||
<input matSliderThumb (valueChange)="runDurationChanged($event)" />
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="accent-color font-medium">Lower latency</div>
|
||||
<div class="accent-color font-medium">Higher throughput</div>
|
||||
</div>
|
||||
@if (isDisabled) {
|
||||
<div class="accent-color font-medium">{{ formatLabel(runDurationIndex) }}</div>
|
||||
} @else {
|
||||
<div class="pl-4 pr-7">
|
||||
<mat-slider [min]="0" [max]="7" [step]="1" showTickMarks discrete [displayWith]="formatLabel">
|
||||
<input matSliderThumb [value]="runDurationIndex" (valueChange)="runDurationChanged($event)" />
|
||||
</mat-slider>
|
||||
</div>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="accent-color font-medium">Lower latency</div>
|
||||
<div class="accent-color font-medium">Higher throughput</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -79,7 +79,7 @@ export class RunDurationSlider implements ControlValueAccessor {
|
|||
if (index < 0) {
|
||||
this.runDurationIndex = 0;
|
||||
} else {
|
||||
this.runDurationIndex = runDuration;
|
||||
this.runDurationIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Remote Process Group</h2>
|
||||
<h2 mat-dialog-title>{{ readonly ? 'Remote Process Group Details' : 'Edit Remote Process Group' }}</h2>
|
||||
<form class="edit-remote-process-group-form" [formGroup]="editRemoteProcessGroupForm">
|
||||
<error-banner></error-banner>
|
||||
<mat-dialog-content>
|
||||
|
@ -31,7 +31,12 @@
|
|||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>URLs</mat-label>
|
||||
<input matInput formControlName="urls" type="text" placeholder="https://remotehost:8443/nifi" />
|
||||
<input
|
||||
matInput
|
||||
formControlName="urls"
|
||||
type="text"
|
||||
placeholder="https://remotehost:8443/nifi"
|
||||
[readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -40,15 +45,15 @@
|
|||
<mat-form-field>
|
||||
<mat-label>Transport Protocol</mat-label>
|
||||
<mat-select formControlName="transportProtocol">
|
||||
<mat-option value="RAW"> RAW</mat-option>
|
||||
<mat-option value="HTTP"> HTTP</mat-option>
|
||||
<mat-option value="RAW" [disabled]="readonly"> RAW</mat-option>
|
||||
<mat-option value="HTTP" [disabled]="readonly"> HTTP</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Local Network Interface</mat-label>
|
||||
<input matInput formControlName="localNetworkInterface" type="text" />
|
||||
<input matInput formControlName="localNetworkInterface" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -56,13 +61,13 @@
|
|||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>HTTP Proxy Server Hostname</mat-label>
|
||||
<input matInput formControlName="httpProxyServerHostname" type="text" />
|
||||
<input matInput formControlName="httpProxyServerHostname" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>HTTP Proxy Server Port</mat-label>
|
||||
<input matInput formControlName="httpProxyServerPort" type="text" />
|
||||
<input matInput formControlName="httpProxyServerPort" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -70,13 +75,13 @@
|
|||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>HTTP Proxy User</mat-label>
|
||||
<input matInput formControlName="httpProxyUser" type="text" />
|
||||
<input matInput formControlName="httpProxyUser" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>HTTP Proxy Password</mat-label>
|
||||
<input matInput formControlName="httpProxyPassword" type="text" />
|
||||
<input matInput formControlName="httpProxyPassword" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -84,28 +89,32 @@
|
|||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Communications Timeout</mat-label>
|
||||
<input matInput formControlName="communicationsTimeout" type="text" />
|
||||
<input matInput formControlName="communicationsTimeout" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Yield Duration</mat-label>
|
||||
<input matInput formControlName="yieldDuration" type="text" />
|
||||
<input matInput formControlName="yieldDuration" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editRemoteProcessGroupForm.dirty || editRemoteProcessGroupForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Add</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editRemoteProcessGroupForm.dirty || editRemoteProcessGroupForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Add</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -29,16 +29,21 @@ describe('EditRemoteProcessGroup', () => {
|
|||
let fixture: ComponentFixture<EditRemoteProcessGroup>;
|
||||
|
||||
const data: any = {
|
||||
revision: {
|
||||
clientId: 'a6482293-7fe8-43b4-8ab4-ee95b3b27721',
|
||||
version: 0
|
||||
},
|
||||
type: ComponentType.RemoteProcessGroup,
|
||||
position: {
|
||||
x: -4,
|
||||
y: -698.5
|
||||
},
|
||||
uri: 'https://localhost:4200/nifi-api/remote-process-groups/abd5a02c-018b-1000-c602-fe83979f1997',
|
||||
entity: {
|
||||
revision: {
|
||||
clientId: 'a6482293-7fe8-43b4-8ab4-ee95b3b27721',
|
||||
version: 0
|
||||
},
|
||||
position: {
|
||||
x: -4,
|
||||
y: -698.5
|
||||
},
|
||||
permissions: {
|
||||
canRead: true,
|
||||
canWrite: false
|
||||
},
|
||||
component: {
|
||||
activeRemoteInputPortCount: 0,
|
||||
activeRemoteOutputPortCount: 0,
|
||||
|
|
|
@ -30,6 +30,7 @@ import { NifiSpinnerDirective } from '../../../../../../../ui/common/spinner/nif
|
|||
import { TextTip } from '../../../../../../../ui/common/tooltips/text-tip/text-tip.component';
|
||||
import { EditComponentDialogRequest } from '../../../../../state/flow';
|
||||
import { ErrorBanner } from '../../../../../../../ui/common/error-banner/error-banner.component';
|
||||
import { CanvasUtils } from '../../../../../service/canvas-utils.service';
|
||||
|
||||
@Component({
|
||||
standalone: true,
|
||||
|
@ -56,12 +57,18 @@ export class EditRemoteProcessGroup {
|
|||
protected readonly TextTip = TextTip;
|
||||
|
||||
editRemoteProcessGroupForm: FormGroup;
|
||||
readonly: boolean;
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public request: EditComponentDialogRequest,
|
||||
private formBuilder: FormBuilder,
|
||||
private canvasUtils: CanvasUtils,
|
||||
private client: Client
|
||||
) {
|
||||
this.readonly =
|
||||
!request.entity.permissions.canWrite ||
|
||||
!this.canvasUtils.remoteProcessGroupSupportsModification(request.entity);
|
||||
|
||||
this.editRemoteProcessGroupForm = this.formBuilder.group({
|
||||
urls: new FormControl(request.entity.component.targetUris, Validators.required),
|
||||
transportProtocol: new FormControl(request.entity.component.transportProtocol, Validators.required),
|
||||
|
|
|
@ -193,7 +193,7 @@
|
|||
port.transmitting === false
|
||||
) {
|
||||
<div
|
||||
class="pointer fa fa-pencil primary-color"
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="configureClicked(port, $event)"
|
||||
title="Edit Port"></div>
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>{{ this.isNew ? 'Add' : 'Edit' }} Parameter Context</h2>
|
||||
<h2 mat-dialog-title>
|
||||
{{ readonly ? 'Parameter Context Details' : isNew ? 'Add Parameter Context' : 'Edit Parameter Context' }}
|
||||
</h2>
|
||||
<form class="parameter-context-edit-form" [formGroup]="editParameterContextForm">
|
||||
<error-banner></error-banner>
|
||||
@if ((updateRequest | async)!; as requestEntity) {
|
||||
|
@ -101,13 +103,18 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input matInput formControlName="name" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Description</mat-label>
|
||||
<textarea matInput formControlName="description" type="text" rows="5"></textarea>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="description"
|
||||
type="text"
|
||||
rows="5"
|
||||
[readonly]="readonly"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -156,15 +163,19 @@
|
|||
} @else {
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editParameterContextForm.dirty || editParameterContextForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editParameterContextForm.dirty || editParameterContextForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,8 @@ export class EditParameterContext {
|
|||
@Output() editParameterContext: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
editParameterContextForm: FormGroup;
|
||||
readonly: boolean;
|
||||
|
||||
isNew: boolean;
|
||||
parameterProvider: ParameterProviderConfiguration | null = null;
|
||||
|
||||
|
@ -92,20 +94,26 @@ export class EditParameterContext {
|
|||
) {
|
||||
if (request.parameterContext) {
|
||||
this.isNew = false;
|
||||
this.readonly = !request.parameterContext.permissions.canWrite;
|
||||
|
||||
this.editParameterContextForm = this.formBuilder.group({
|
||||
name: new FormControl(request.parameterContext.component.name, Validators.required),
|
||||
description: new FormControl(request.parameterContext.component.description),
|
||||
parameters: new FormControl(request.parameterContext.component.parameters),
|
||||
inheritedParameterContexts: new FormControl(
|
||||
request.parameterContext.component.inheritedParameterContexts
|
||||
)
|
||||
parameters: new FormControl({
|
||||
value: request.parameterContext.component.parameters,
|
||||
disabled: this.readonly
|
||||
}),
|
||||
inheritedParameterContexts: new FormControl({
|
||||
value: request.parameterContext.component.inheritedParameterContexts,
|
||||
disabled: this.readonly
|
||||
})
|
||||
});
|
||||
if (request.parameterContext.component.parameterProviderConfiguration) {
|
||||
this.parameterProvider = request.parameterContext.component.parameterProviderConfiguration.component;
|
||||
}
|
||||
} else {
|
||||
this.isNew = true;
|
||||
this.readonly = false;
|
||||
|
||||
this.editParameterContextForm = this.formBuilder.group({
|
||||
name: new FormControl('', Validators.required),
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.parameter-context-draggable-item {
|
||||
|
|
|
@ -25,21 +25,6 @@
|
|||
(matSortChange)="sortData($event)"
|
||||
[matSortActive]="initialSortColumn"
|
||||
[matSortDirection]="initialSortDirection">
|
||||
<!-- More Details Column -->
|
||||
<ng-container matColumnDef="moreDetails">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
@if (canRead(item)) {
|
||||
<div class="flex items-center gap-x-2">
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
@if (canRead(item)) {
|
||||
<div class="pointer fa fa-info-circle primary-color"></div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header>Name</th>
|
||||
|
@ -73,11 +58,11 @@
|
|||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<div class="flex items-center justify-end gap-x-2">
|
||||
@if (canRead(item) && canWrite(item)) {
|
||||
@if (canRead(item)) {
|
||||
<div
|
||||
class="pointer fa fa-pencil primary-color"
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="editClicked(item, $event)"
|
||||
title="Edit"></div>
|
||||
[title]="canWrite(item) ? 'Edit' : 'View Configuration'"></div>
|
||||
}
|
||||
@if (canDelete(item)) {
|
||||
<div
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
|
||||
.parameter-context-table {
|
||||
.listing-table {
|
||||
.mat-column-moreDetails {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.mat-column-name {
|
||||
width: 25%;
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export class ParameterContextTable {
|
|||
@Output() editParameterContext: EventEmitter<ParameterContextEntity> = new EventEmitter<ParameterContextEntity>();
|
||||
@Output() deleteParameterContext: EventEmitter<ParameterContextEntity> = new EventEmitter<ParameterContextEntity>();
|
||||
|
||||
displayedColumns: string[] = ['moreDetails', 'name', 'provider', 'description', 'actions'];
|
||||
displayedColumns: string[] = ['name', 'provider', 'description', 'actions'];
|
||||
dataSource: MatTableDataSource<ParameterContextEntity> = new MatTableDataSource<ParameterContextEntity>();
|
||||
|
||||
constructor(private nifiCommon: NiFiCommon) {}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<div class="parameter-table flex gap-x-3">
|
||||
<div class="flex basis-2/3 flex-col gap-y-3">
|
||||
@if (canAddParameters) {
|
||||
@if (canAddParameters && !isDisabled) {
|
||||
<div class="flex justify-end items-center">
|
||||
<button mat-icon-button color="primary" type="button" (click)="newParameterClicked()">
|
||||
<i class="fa fa-plus"></i>
|
||||
|
@ -101,13 +101,13 @@
|
|||
mat-dialog-close="ROUTED"
|
||||
title="Go to"></div>
|
||||
}
|
||||
@if (canEdit(item)) {
|
||||
@if (canEdit(item) && !isDisabled) {
|
||||
<div
|
||||
class="pointer fa fa-pencil primary-color"
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="editClicked(item)"
|
||||
title="Edit"></div>
|
||||
}
|
||||
@if (canDelete(item)) {
|
||||
@if (canDelete(item) && !isDisabled) {
|
||||
<div
|
||||
class="pointer fa fa-trash primary-color"
|
||||
(click)="deleteClicked(item)"
|
||||
|
|
|
@ -109,7 +109,6 @@ export class ParameterTable implements AfterViewInit, ControlValueAccessor {
|
|||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
// TODO - update component to disable controls accordingly
|
||||
this.isDisabled = isDisabled;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,16 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Flow Analysis Rule</h2>
|
||||
<h2 mat-dialog-title>
|
||||
<div class="flex justify-between items-baseline">
|
||||
<div>
|
||||
{{ readonly ? 'Flow Analysis Rule Details' : 'Edit Flow Analysis Rule' }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ formatType(request.flowAnalysisRule) }}
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<form class="flow-analysis-rule-edit-form" [formGroup]="editFlowAnalysisRuleForm">
|
||||
<error-banner></error-banner>
|
||||
<mat-dialog-content>
|
||||
|
@ -25,7 +34,7 @@
|
|||
<div class="w-full">
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input matInput formControlName="name" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
<div class="flex flex-col mb-5">
|
||||
<div>Id</div>
|
||||
|
@ -54,6 +63,7 @@
|
|||
@for (option of strategies; track option) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="readonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getPropertyTipData(option)"
|
||||
|
@ -82,7 +92,12 @@
|
|||
<div class="tab-content py-4">
|
||||
<mat-form-field>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments" type="text" rows="5"></textarea>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="comments"
|
||||
type="text"
|
||||
rows="5"
|
||||
[readonly]="readonly"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
@ -90,15 +105,19 @@
|
|||
</mat-dialog-content>
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editFlowAnalysisRuleForm.dirty || editFlowAnalysisRuleForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editFlowAnalysisRuleForm.dirty || editFlowAnalysisRuleForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
h2.mdc-dialog__title::before {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.flow-analysis-rule-edit-form {
|
||||
@include mat.button-density(-1);
|
||||
|
||||
|
|
|
@ -79,6 +79,7 @@ export class EditFlowAnalysisRule {
|
|||
new EventEmitter<UpdateFlowAnalysisRuleRequest>();
|
||||
|
||||
editFlowAnalysisRuleForm: FormGroup;
|
||||
readonly: boolean;
|
||||
|
||||
strategies: SelectOption[] = [
|
||||
{
|
||||
|
@ -95,6 +96,9 @@ export class EditFlowAnalysisRule {
|
|||
private nifiCommon: NiFiCommon,
|
||||
private clusterConnectionService: ClusterConnectionService
|
||||
) {
|
||||
this.readonly =
|
||||
!request.flowAnalysisRule.permissions.canWrite || request.flowAnalysisRule.status.runStatus !== 'DISABLED';
|
||||
|
||||
const serviceProperties: any = request.flowAnalysisRule.component.properties;
|
||||
const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => {
|
||||
const [property, value] = entry;
|
||||
|
@ -110,7 +114,7 @@ export class EditFlowAnalysisRule {
|
|||
name: new FormControl(request.flowAnalysisRule.component.name, Validators.required),
|
||||
state: new FormControl(request.flowAnalysisRule.component.state === 'STOPPED', Validators.required),
|
||||
enforcementPolicy: new FormControl('ENFORCE', Validators.required),
|
||||
properties: new FormControl(properties),
|
||||
properties: new FormControl({ value: properties, disabled: this.readonly }),
|
||||
comments: new FormControl(request.flowAnalysisRule.component.comments)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
class="pointer fa fa-book primary-color"
|
||||
(click)="viewDocumentationClicked(item, $event)"
|
||||
title="View Documentation"></div>
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
@if (hasComments(item)) {
|
||||
<div
|
||||
class="pointer fa fa-comment primary-color"
|
||||
|
@ -116,13 +115,10 @@
|
|||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<div class="flex items-center justify-end gap-x-2">
|
||||
@if (canConfigure(item)) {
|
||||
<div
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="configureClicked(item, $event)"
|
||||
title="Edit"></div>
|
||||
}
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
<div
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="configureClicked(item, $event)"
|
||||
[title]="canConfigure(item) ? 'Edit' : 'View Configuration'"></div>
|
||||
@if (canDisable(item)) {
|
||||
<div
|
||||
class="pointer fa icon icon-enable-false primary-color"
|
||||
|
|
|
@ -15,7 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Parameter Provider</h2>
|
||||
<h2 mat-dialog-title>
|
||||
<div class="flex justify-between items-baseline">
|
||||
<div>Edit Parameter Provider</div>
|
||||
<div class="text-sm">
|
||||
{{ formatType(request.parameterProvider) }}
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<form class="parameter-provider-edit-form" [formGroup]="editParameterProviderForm">
|
||||
<error-banner></error-banner>
|
||||
<mat-dialog-content>
|
||||
|
@ -26,7 +33,7 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input matInput formControlName="name" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="flex flex-col mb-5">
|
||||
|
@ -80,7 +87,12 @@
|
|||
<div class="tab-content py-4">
|
||||
<mat-form-field>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments" type="text" rows="5"></textarea>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="comments"
|
||||
type="text"
|
||||
rows="5"
|
||||
[readonly]="readonly"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
@ -89,15 +101,19 @@
|
|||
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editParameterProviderForm.dirty || editParameterProviderForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editParameterProviderForm.dirty || editParameterProviderForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
h2.mdc-dialog__title::before {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.parameter-provider-edit-form {
|
||||
@include mat.button-density(-1);
|
||||
|
||||
|
|
|
@ -76,6 +76,7 @@ export class EditParameterProvider {
|
|||
new EventEmitter<UpdateParameterProviderRequest>();
|
||||
|
||||
editParameterProviderForm: FormGroup;
|
||||
readonly: boolean;
|
||||
|
||||
constructor(
|
||||
@Inject(MAT_DIALOG_DATA) public request: EditParameterProviderRequest,
|
||||
|
@ -84,6 +85,8 @@ export class EditParameterProvider {
|
|||
private nifiCommon: NiFiCommon,
|
||||
private clusterConnectionService: ClusterConnectionService
|
||||
) {
|
||||
this.readonly = !request.parameterProvider.permissions.canWrite;
|
||||
|
||||
const providerProperties = request.parameterProvider.component.properties;
|
||||
const properties: Property[] = Object.entries(providerProperties).map((entry: any) => {
|
||||
const [property, value] = entry;
|
||||
|
@ -97,7 +100,7 @@ export class EditParameterProvider {
|
|||
// build the form
|
||||
this.editParameterProviderForm = this.formBuilder.group({
|
||||
name: new FormControl(request.parameterProvider.component.name, Validators.required),
|
||||
properties: new FormControl(properties),
|
||||
properties: new FormControl({ value: properties, disabled: this.readonly }),
|
||||
comments: new FormControl(request.parameterProvider.component.comments)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -33,9 +33,6 @@
|
|||
<td mat-cell *matCellDef="let item">
|
||||
@if (canRead(item)) {
|
||||
<div class="flex items-center gap-x-2">
|
||||
<!-- TODO: open details -->
|
||||
<div class="pointer fa fa-info-circle primary-color" title="View details"></div>
|
||||
|
||||
<div
|
||||
class="pointer fa fa-book primary-color"
|
||||
(click)="viewDocumentationClicked(item, $event)"
|
||||
|
@ -96,11 +93,11 @@
|
|||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<div class="flex items-center justify-end gap-x-2">
|
||||
@if (canConfigure(item)) {
|
||||
@if (canRead(item)) {
|
||||
<div
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="configureClicked(item, $event)"
|
||||
title="Edit"></div>
|
||||
[title]="canWrite(item) ? 'Edit' : 'View Configuration'"></div>
|
||||
}
|
||||
@if (hasAdvancedUi(item)) {
|
||||
<div
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
.listing-table {
|
||||
table {
|
||||
.mat-column-moreDetails {
|
||||
width: 82px;
|
||||
width: 52px;
|
||||
}
|
||||
|
||||
.mat-column-name {
|
||||
|
|
|
@ -97,10 +97,6 @@ export class ParameterProvidersTable {
|
|||
return this.flowConfiguration.supportsManagedAuthorizer && this.currentUser.tenantsPermissions.canRead;
|
||||
}
|
||||
|
||||
canConfigure(entity: ParameterProviderEntity): boolean {
|
||||
return this.canRead(entity) && this.canWrite(entity);
|
||||
}
|
||||
|
||||
hasAdvancedUi(entity: ParameterProviderEntity): boolean {
|
||||
return this.canRead(entity) && !!entity.component.customUiUrl;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,14 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Registry Client</h2>
|
||||
<h2 mat-dialog-title>
|
||||
<div class="flex justify-between items-baseline">
|
||||
<div>Edit Registry Client</div>
|
||||
<div class="text-sm">
|
||||
{{ formatType(request.registryClient) }}
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<form class="edit-registry-client-form" [formGroup]="editRegistryClientForm">
|
||||
<error-banner></error-banner>
|
||||
<mat-dialog-content>
|
||||
|
@ -35,9 +42,9 @@
|
|||
</mat-form-field>
|
||||
</div>
|
||||
<div class="flex flex-col mb-5">
|
||||
<div>Id</div>
|
||||
<div>Type</div>
|
||||
<div class="accent-color font-medium">
|
||||
{{ request.registryClient.component.type }}
|
||||
{{ formatType(request.registryClient) }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
h2.mdc-dialog__title::before {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.edit-registry-client-form {
|
||||
@include mat.button-density(-1);
|
||||
|
||||
|
|
|
@ -24,12 +24,11 @@ import { MatButtonModule } from '@angular/material/button';
|
|||
import { AsyncPipe } from '@angular/common';
|
||||
import { Observable } from 'rxjs';
|
||||
import {
|
||||
DocumentedType,
|
||||
InlineServiceCreationRequest,
|
||||
InlineServiceCreationResponse,
|
||||
Parameter,
|
||||
Property,
|
||||
TextTipInput
|
||||
RegistryClientEntity
|
||||
} from '../../../../../state/shared';
|
||||
import { EditRegistryClientDialogRequest, EditRegistryClientRequest } from '../../../state/registry-clients';
|
||||
import { NifiSpinnerDirective } from '../../../../../ui/common/spinner/nifi-spinner.directive';
|
||||
|
@ -101,15 +100,8 @@ export class EditRegistryClient {
|
|||
});
|
||||
}
|
||||
|
||||
formatType(option: DocumentedType): string {
|
||||
return this.nifiCommon.substringAfterLast(option.type, '.');
|
||||
}
|
||||
|
||||
getOptionTipData(option: DocumentedType): TextTipInput {
|
||||
return {
|
||||
// @ts-ignore
|
||||
text: option.description
|
||||
};
|
||||
formatType(entity: RegistryClientEntity): string {
|
||||
return this.nifiCommon.formatType(entity.component);
|
||||
}
|
||||
|
||||
submitForm(postUpdateNavigation?: string[]) {
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
<td mat-cell *matCellDef="let item">
|
||||
@if (canRead(item)) {
|
||||
<div class="flex items-center gap-x-2">
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
@if (hasErrors(item)) {
|
||||
<div
|
||||
class="mr-3 pointer fa fa-warning has-errors"
|
||||
|
|
|
@ -15,7 +15,16 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Reporting Task</h2>
|
||||
<h2 mat-dialog-title>
|
||||
<div class="flex justify-between items-baseline">
|
||||
<div>
|
||||
{{ readonly ? 'Reporting Task Details' : 'Edit Reporting Task' }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ formatType(request.reportingTask) }}
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<form class="reporting-task-edit-form" [formGroup]="editReportingTaskForm">
|
||||
<error-banner></error-banner>
|
||||
<mat-dialog-content>
|
||||
|
@ -26,9 +35,11 @@
|
|||
<div class="flex">
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input matInput formControlName="name" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
<mat-checkbox color="primary" formControlName="state" class="pl-1"> Enabled </mat-checkbox>
|
||||
<mat-checkbox color="primary" formControlName="state" class="ml-1 mt-2">
|
||||
Enabled
|
||||
</mat-checkbox>
|
||||
</div>
|
||||
<div class="flex flex-col mb-5">
|
||||
<div>Id</div>
|
||||
|
@ -59,6 +70,7 @@
|
|||
@for (option of strategies; track option) {
|
||||
<mat-option
|
||||
[value]="option.value"
|
||||
[disabled]="readonly"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
[tooltipInputData]="getPropertyTipData(option)"
|
||||
|
@ -76,7 +88,8 @@
|
|||
matInput
|
||||
formControlName="schedulingPeriod"
|
||||
(change)="schedulingPeriodChanged()"
|
||||
type="text" />
|
||||
type="text"
|
||||
[readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -100,7 +113,12 @@
|
|||
<div class="tab-content py-4">
|
||||
<mat-form-field>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments" type="text" rows="5"></textarea>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="comments"
|
||||
type="text"
|
||||
rows="5"
|
||||
[readonly]="readonly"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
@ -108,15 +126,19 @@
|
|||
</mat-dialog-content>
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editReportingTaskForm.dirty || editReportingTaskForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editReportingTaskForm.dirty || editReportingTaskForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
h2.mdc-dialog__title::before {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.reporting-task-edit-form {
|
||||
@include mat.button-density(-1);
|
||||
|
||||
|
|
|
@ -83,6 +83,8 @@ export class EditReportingTask {
|
|||
new EventEmitter<UpdateReportingTaskRequest>();
|
||||
|
||||
editReportingTaskForm: FormGroup;
|
||||
readonly: boolean;
|
||||
|
||||
schedulingStrategy: string;
|
||||
cronDrivenSchedulingPeriod: string;
|
||||
timerDrivenSchedulingPeriod: string;
|
||||
|
@ -108,6 +110,11 @@ export class EditReportingTask {
|
|||
private nifiCommon: NiFiCommon,
|
||||
private clusterConnectionService: ClusterConnectionService
|
||||
) {
|
||||
this.readonly =
|
||||
!request.reportingTask.permissions.canWrite ||
|
||||
(request.reportingTask.status.runStatus !== 'STOPPED' &&
|
||||
request.reportingTask.status.runStatus !== 'DISABLED');
|
||||
|
||||
const serviceProperties: any = request.reportingTask.component.properties;
|
||||
const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => {
|
||||
const [property, value] = entry;
|
||||
|
@ -137,13 +144,16 @@ export class EditReportingTask {
|
|||
// build the form
|
||||
this.editReportingTaskForm = this.formBuilder.group({
|
||||
name: new FormControl(request.reportingTask.component.name, Validators.required),
|
||||
state: new FormControl(request.reportingTask.component.state === 'STOPPED', Validators.required),
|
||||
state: new FormControl(
|
||||
{ value: request.reportingTask.component.state !== 'DISABLED', disabled: this.readonly },
|
||||
Validators.required
|
||||
),
|
||||
schedulingStrategy: new FormControl(
|
||||
request.reportingTask.component.schedulingStrategy,
|
||||
Validators.required
|
||||
),
|
||||
schedulingPeriod: new FormControl(schedulingPeriod, Validators.required),
|
||||
properties: new FormControl(properties),
|
||||
properties: new FormControl({ value: properties, disabled: this.readonly }),
|
||||
comments: new FormControl(request.reportingTask.component.comments)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
class="pointer fa fa-book primary-color"
|
||||
(click)="viewDocumentationClicked(item, $event)"
|
||||
title="View Documentation"></div>
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
@if (hasComments(item)) {
|
||||
<div
|
||||
class="pointer fa fa-comment primary-color"
|
||||
|
@ -112,24 +111,22 @@
|
|||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<div class="flex items-center justify-end gap-x-2">
|
||||
@if (canStop(item)) {
|
||||
<div
|
||||
class="pointer fa fa-stop primary-color"
|
||||
(click)="stopClicked(item)"
|
||||
title="Stop"></div>
|
||||
}
|
||||
@if (canEdit(item)) {
|
||||
<div
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="configureClicked(item, $event)"
|
||||
title="Edit"></div>
|
||||
}
|
||||
<div
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="configureClicked(item, $event)"
|
||||
[title]="canEdit(item) ? 'Edit' : 'View Configuration'"></div>
|
||||
@if (hasAdvancedUi(item)) {
|
||||
<div
|
||||
class="pointer fa fa-cogs primary-color"
|
||||
(click)="advancedClicked(item, $event)"
|
||||
title="Advanced"></div>
|
||||
}
|
||||
@if (canStop(item)) {
|
||||
<div
|
||||
class="pointer fa fa-stop primary-color"
|
||||
(click)="stopClicked(item)"
|
||||
title="Stop"></div>
|
||||
}
|
||||
@if (canStart(item)) {
|
||||
<div
|
||||
class="pointer fa fa-play primary-color"
|
||||
|
|
|
@ -81,7 +81,7 @@
|
|||
<div class="flex items-center justify-end gap-x-2">
|
||||
@if (canEditOrDelete(currentUser, item)) {
|
||||
<div
|
||||
class="pointer fa fa-pencil primary-color"
|
||||
class="pointer fa fa-cog primary-color"
|
||||
title="Edit"
|
||||
(click)="editClicked(item, $event)"></div>
|
||||
}
|
||||
|
|
|
@ -106,7 +106,8 @@
|
|||
class="pointer fa fa-sticky-note-o primary-color"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="BulletinsTip"
|
||||
[tooltipInputData]="getBulletinsTipData(reference)"></div>
|
||||
[tooltipInputData]="getBulletinsTipData(reference)"
|
||||
[delayClose]="false"></div>
|
||||
}
|
||||
@if (hasActiveThreads(reference.component)) {
|
||||
<div>({{ reference.component.activeThreadCount }})</div>
|
||||
|
|
|
@ -118,19 +118,16 @@
|
|||
<td mat-cell *matCellDef="let item">
|
||||
@if (definedByCurrentGroup(item)) {
|
||||
<div class="flex items-center justify-end gap-x-2">
|
||||
@if (canConfigure(item)) {
|
||||
<div
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="configureClicked(item, $event)"
|
||||
title="Edit"></div>
|
||||
}
|
||||
<div
|
||||
class="pointer fa fa-cog primary-color"
|
||||
(click)="configureClicked(item, $event)"
|
||||
[title]="canConfigure(item) ? 'Edit' : 'View Configuration'"></div>
|
||||
@if (hasAdvancedUi(item)) {
|
||||
<div
|
||||
class="pointer fa fa-cogs primary-color"
|
||||
(click)="advancedClicked(item, $event)"
|
||||
title="Advanced"></div>
|
||||
}
|
||||
<!-- TODO - handle read only in configure component? -->
|
||||
@if (canDisable(item)) {
|
||||
<div
|
||||
class="pointer icon icon-enable-false primary-color"
|
||||
|
|
|
@ -15,7 +15,16 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<h2 mat-dialog-title>Edit Controller Service</h2>
|
||||
<h2 mat-dialog-title>
|
||||
<div class="flex justify-between items-baseline">
|
||||
<div>
|
||||
{{ readonly ? 'Controller Service Details' : 'Edit Controller Service' }}
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
{{ formatType(request.controllerService) }}
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<form class="controller-service-edit-form" [formGroup]="editControllerServiceForm">
|
||||
<error-banner></error-banner>
|
||||
<mat-dialog-content>
|
||||
|
@ -26,7 +35,7 @@
|
|||
<div>
|
||||
<mat-form-field>
|
||||
<mat-label>Name</mat-label>
|
||||
<input matInput formControlName="name" type="text" />
|
||||
<input matInput formControlName="name" type="text" [readonly]="readonly" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="flex flex-col mb-5">
|
||||
|
@ -69,7 +78,7 @@
|
|||
<mat-label>Bulletin Level</mat-label>
|
||||
<mat-select formControlName="bulletinLevel">
|
||||
@for (option of bulletinLevels; track option) {
|
||||
<mat-option [value]="option.value">
|
||||
<mat-option [value]="option.value" [disabled]="readonly">
|
||||
{{ option.text }}
|
||||
</mat-option>
|
||||
}
|
||||
|
@ -112,7 +121,12 @@
|
|||
<div class="tab-content py-4">
|
||||
<mat-form-field>
|
||||
<mat-label>Comments</mat-label>
|
||||
<textarea matInput formControlName="comments" type="text" rows="5"></textarea>
|
||||
<textarea
|
||||
matInput
|
||||
formControlName="comments"
|
||||
type="text"
|
||||
rows="5"
|
||||
[readonly]="readonly"></textarea>
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
@ -120,15 +134,19 @@
|
|||
</mat-dialog-content>
|
||||
@if ({ value: (saving$ | async)! }; as saving) {
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editControllerServiceForm.dirty || editControllerServiceForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button mat-button mat-dialog-close color="primary">Close</button>
|
||||
} @else {
|
||||
<button mat-button mat-dialog-close>Cancel</button>
|
||||
<button
|
||||
[disabled]="!editControllerServiceForm.dirty || editControllerServiceForm.invalid || saving.value"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="submitForm()"
|
||||
mat-button>
|
||||
<span *nifiSpinner="saving.value">Apply</span>
|
||||
</button>
|
||||
}
|
||||
</mat-dialog-actions>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
|
||||
@use '@angular/material' as mat;
|
||||
|
||||
h2.mdc-dialog__title::before {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.controller-service-edit-form {
|
||||
@include mat.button-density(-1);
|
||||
|
||||
|
|
|
@ -82,6 +82,7 @@ export class EditControllerService {
|
|||
new EventEmitter<UpdateControllerServiceRequest>();
|
||||
|
||||
editControllerServiceForm: FormGroup;
|
||||
readonly: boolean;
|
||||
|
||||
bulletinLevels = [
|
||||
{
|
||||
|
@ -113,6 +114,10 @@ export class EditControllerService {
|
|||
private nifiCommon: NiFiCommon,
|
||||
private clusterConnectionService: ClusterConnectionService
|
||||
) {
|
||||
this.readonly =
|
||||
!request.controllerService.permissions.canWrite ||
|
||||
request.controllerService.status.runStatus !== 'DISABLED';
|
||||
|
||||
const serviceProperties: any = request.controllerService.component.properties;
|
||||
const properties: Property[] = Object.entries(serviceProperties).map((entry: any) => {
|
||||
const [property, value] = entry;
|
||||
|
@ -127,7 +132,7 @@ export class EditControllerService {
|
|||
this.editControllerServiceForm = this.formBuilder.group({
|
||||
name: new FormControl(request.controllerService.component.name, Validators.required),
|
||||
bulletinLevel: new FormControl(request.controllerService.component.bulletinLevel, Validators.required),
|
||||
properties: new FormControl(properties),
|
||||
properties: new FormControl({ value: properties, disabled: this.readonly }),
|
||||
comments: new FormControl(request.controllerService.component.comments)
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
<ng-container *ngIf="allowableValue.description; else noDescription">
|
||||
<mat-option
|
||||
[value]="allowableValue.id"
|
||||
[disabled]="readonly"
|
||||
(mousedown)="preventDrag($event)"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
|
@ -45,7 +46,10 @@
|
|||
</mat-option>
|
||||
</ng-container>
|
||||
<ng-template #noDescription>
|
||||
<mat-option [value]="allowableValue.id" (mousedown)="preventDrag($event)">
|
||||
<mat-option
|
||||
[value]="allowableValue.id"
|
||||
[disabled]="readonly"
|
||||
(mousedown)="preventDrag($event)">
|
||||
<span
|
||||
class="option-text"
|
||||
[class.nifi-surface-default]="allowableValue.value == null"
|
||||
|
@ -73,6 +77,7 @@
|
|||
<ng-container *ngIf="parameterAllowableValue.description; else noDescription">
|
||||
<mat-option
|
||||
[value]="parameterAllowableValue.id"
|
||||
[disabled]="readonly"
|
||||
(mousedown)="preventDrag($event)"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="TextTip"
|
||||
|
@ -87,7 +92,10 @@
|
|||
</mat-option>
|
||||
</ng-container>
|
||||
<ng-template #noDescription>
|
||||
<mat-option [value]="parameterAllowableValue.id" (mousedown)="preventDrag($event)">
|
||||
<mat-option
|
||||
[value]="parameterAllowableValue.id"
|
||||
[disabled]="readonly"
|
||||
(mousedown)="preventDrag($event)">
|
||||
<span
|
||||
class="option-text"
|
||||
[class.unset]="parameterAllowableValue.value == null"
|
||||
|
@ -102,18 +110,29 @@
|
|||
</ng-template>
|
||||
</div>
|
||||
<div class="flex justify-end items-center gap-x-2">
|
||||
<button mat-button type="button" (mousedown)="preventDrag($event)" (click)="cancelClicked()">
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
[disabled]="!comboEditorForm.dirty || comboEditorForm.invalid"
|
||||
(mousedown)="preventDrag($event)"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="okClicked()"
|
||||
mat-button>
|
||||
Ok
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button
|
||||
mat-button
|
||||
type="button"
|
||||
color="primary"
|
||||
(mousedown)="preventDrag($event)"
|
||||
(click)="cancelClicked()">
|
||||
Close
|
||||
</button>
|
||||
} @else {
|
||||
<button mat-button type="button" (mousedown)="preventDrag($event)" (click)="cancelClicked()">
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
[disabled]="!comboEditorForm.dirty || comboEditorForm.invalid"
|
||||
(mousedown)="preventDrag($event)"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="okClicked()"
|
||||
mat-button>
|
||||
Ok
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -84,6 +84,7 @@ export class ComboEditor {
|
|||
this.initialAllowableValues();
|
||||
}
|
||||
@Input() width!: number;
|
||||
@Input() readonly: boolean = false;
|
||||
|
||||
@Output() ok: EventEmitter<any> = new EventEmitter<any>();
|
||||
@Output() cancel: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<div class="property-editor p-4" [style.width.px]="width" cdkDrag resizable (resized)="resized()">
|
||||
<div class="property-editor p-4 h-full" [style.width.px]="width" cdkDrag resizable (resized)="resized()">
|
||||
<form class="h-full" [formGroup]="nfEditorForm" cdkTrapFocus cdkTrapFocusAutoCapture>
|
||||
<div class="flex flex-col gap-y-3 h-full">
|
||||
<div class="flex justify-end">
|
||||
|
@ -50,27 +50,40 @@
|
|||
(mousedown)="preventDrag($event)"
|
||||
(codeMirrorLoaded)="codeMirrorLoaded($event)"></ngx-codemirror>
|
||||
</div>
|
||||
<mat-checkbox
|
||||
color="primary"
|
||||
formControlName="setEmptyString"
|
||||
(mousedown)="preventDrag($event)"
|
||||
(change)="setEmptyStringChanged()"
|
||||
>Set empty string</mat-checkbox
|
||||
>
|
||||
@if (!readonly) {
|
||||
<mat-checkbox
|
||||
color="primary"
|
||||
formControlName="setEmptyString"
|
||||
(mousedown)="preventDrag($event)"
|
||||
(change)="setEmptyStringChanged()"
|
||||
>Set empty string
|
||||
</mat-checkbox>
|
||||
}
|
||||
</div>
|
||||
<div class="flex justify-end items-center gap-x-2">
|
||||
<button mat-button type="button" (mousedown)="preventDrag($event)" (click)="cancelClicked()">
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
[disabled]="!nfEditorForm.dirty || nfEditorForm.invalid"
|
||||
(mousedown)="preventDrag($event)"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="okClicked()"
|
||||
mat-button>
|
||||
Ok
|
||||
</button>
|
||||
@if (readonly) {
|
||||
<button
|
||||
mat-button
|
||||
type="button"
|
||||
color="primary"
|
||||
(mousedown)="preventDrag($event)"
|
||||
(click)="cancelClicked()">
|
||||
Close
|
||||
</button>
|
||||
} @else {
|
||||
<button mat-button type="button" (mousedown)="preventDrag($event)" (click)="cancelClicked()">
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
[disabled]="!nfEditorForm.dirty || nfEditorForm.invalid"
|
||||
(mousedown)="preventDrag($event)"
|
||||
type="button"
|
||||
color="primary"
|
||||
(click)="okClicked()"
|
||||
mat-button>
|
||||
Ok
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -82,6 +82,7 @@ export class NfEditor implements OnDestroy {
|
|||
this.loadParameters();
|
||||
}
|
||||
@Input() width!: number;
|
||||
@Input() readonly: boolean = false;
|
||||
|
||||
@Output() ok: EventEmitter<string> = new EventEmitter<string>();
|
||||
@Output() cancel: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
@ -116,7 +117,7 @@ export class NfEditor implements OnDestroy {
|
|||
|
||||
codeMirrorLoaded(codeEditor: any): void {
|
||||
this.editor = codeEditor.codeMirror;
|
||||
this.editor.setSize('100%', 100);
|
||||
this.editor.setSize('100%', '100%');
|
||||
this.editor.execCommand('selectAll');
|
||||
}
|
||||
|
||||
|
@ -161,6 +162,7 @@ export class NfEditor implements OnDestroy {
|
|||
getOptions(): any {
|
||||
return {
|
||||
mode: this.mode,
|
||||
readOnly: this.readonly,
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
extraKeys: {
|
||||
|
|
|
@ -18,12 +18,14 @@
|
|||
<div class="property-table flex flex-col gap-y-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="font-bold">Required field</div>
|
||||
<div>
|
||||
<!-- TODO Property Verification -->
|
||||
<button mat-icon-button color="primary" type="button" (click)="newPropertyClicked()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
@if (!isDisabled) {
|
||||
<div>
|
||||
<!-- TODO Property Verification -->
|
||||
<button mat-icon-button color="primary" type="button" (click)="newPropertyClicked()">
|
||||
<i class="fa fa-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="listing-table">
|
||||
<div class="h-96 overflow-y-auto overflow-x-hidden">
|
||||
|
@ -101,18 +103,6 @@
|
|||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let item">
|
||||
<div class="flex items-center justify-end gap-x-2">
|
||||
@if (item.descriptor.identifiesControllerService) {
|
||||
<div
|
||||
class="pointer fa fa-plus primary-color"
|
||||
(click)="createNewControllerService(item)"
|
||||
title="Create new service"></div>
|
||||
}
|
||||
@if (canConvertToParameter(item)) {
|
||||
<div
|
||||
class="pointer fa fa-level-up primary-color"
|
||||
(click)="convertToParameterClicked(item)"
|
||||
title="Convert to Parameter"></div>
|
||||
}
|
||||
@if (canGoToParameter(item)) {
|
||||
<div
|
||||
class="pointer fa fa-long-arrow-right primary-color"
|
||||
|
@ -125,11 +115,25 @@
|
|||
(click)="goToServiceClicked(item)"
|
||||
title="Go to Service"></div>
|
||||
}
|
||||
@if (item.type == 'userDefined') {
|
||||
<div
|
||||
class="pointer fa fa-trash primary-color"
|
||||
(click)="deleteProperty(item)"
|
||||
title="Delete"></div>
|
||||
@if (!isDisabled) {
|
||||
@if (item.descriptor.identifiesControllerService) {
|
||||
<div
|
||||
class="pointer fa fa-plus primary-color"
|
||||
(click)="createNewControllerService(item)"
|
||||
title="Create new service"></div>
|
||||
}
|
||||
@if (canConvertToParameter(item)) {
|
||||
<div
|
||||
class="pointer fa fa-level-up primary-color"
|
||||
(click)="convertToParameterClicked(item)"
|
||||
title="Convert to Parameter"></div>
|
||||
}
|
||||
@if (item.type == 'userDefined') {
|
||||
<div
|
||||
class="pointer fa fa-trash primary-color"
|
||||
(click)="deleteProperty(item)"
|
||||
title="Delete"></div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -157,6 +161,7 @@
|
|||
[item]="editorItem"
|
||||
[getParameters]="getParameters"
|
||||
[width]="editorWidth"
|
||||
[readonly]="isDisabled"
|
||||
(ok)="savePropertyValue(editorItem, $event)"
|
||||
(cancel)="closeEditor()"></combo-editor>
|
||||
} @else {
|
||||
|
@ -164,6 +169,7 @@
|
|||
[item]="editorItem"
|
||||
[getParameters]="getParameters"
|
||||
[width]="editorWidth"
|
||||
[readonly]="isDisabled"
|
||||
(ok)="savePropertyValue(editorItem, $event)"
|
||||
(cancel)="closeEditor()"></nf-editor>
|
||||
}
|
||||
|
|
|
@ -244,7 +244,6 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor {
|
|||
}
|
||||
|
||||
setDisabledState(isDisabled: boolean): void {
|
||||
// TODO - update component to disable controls accordingly
|
||||
this.isDisabled = isDisabled;
|
||||
}
|
||||
|
||||
|
@ -410,8 +409,8 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor {
|
|||
this.editorOpen = true;
|
||||
|
||||
if (this.hasAllowableValues(item)) {
|
||||
this.editorWidth = width;
|
||||
this.editorOffsetX = -24;
|
||||
this.editorWidth = width + 50;
|
||||
this.editorOffsetX = 0;
|
||||
this.editorOffsetY = 24;
|
||||
} else {
|
||||
this.editorWidth = width + 100;
|
||||
|
@ -431,8 +430,6 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor {
|
|||
}
|
||||
|
||||
canGoToService(item: PropertyItem): boolean {
|
||||
// TODO - add Input() for supportsGoTo? currently only false in summary table
|
||||
|
||||
const descriptor: PropertyDescriptor = item.descriptor;
|
||||
if (item.value && descriptor.identifiesControllerService && descriptor.allowableValues) {
|
||||
return descriptor.allowableValues.some(
|
||||
|
|
|
@ -100,6 +100,10 @@
|
|||
font-weight: normal !important;
|
||||
}
|
||||
|
||||
.mat-mdc-tab-header {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.mat-mdc-icon-button {
|
||||
--mdc-icon-button-state-layer-size: 28px;
|
||||
--mdc-icon-button-icon-size: 14px;
|
||||
|
|
Loading…
Reference in New Issue