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

scottyaslan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 7945234f5a NIFI-13670: Improve sensitive value editing. (#9191)
7945234f5a is described below

commit 7945234f5a7e2eb7a7214ac893c0bbce14cb69c8
Author: Matt Gilman <[email protected]>
AuthorDate: Mon Aug 26 13:56:02 2024 -0400

    NIFI-13670: Improve sensitive value editing. (#9191)
    
    * NIFI-13670: Improve sensitive value editing.
    - Do not present the user with the masked value.
    - Do not use the masked value when determining if a value has changed.
    - Do not include the masked value in the payload if the value hasn't 
changed.
    - Updates to ParameterItem model to more easily track changes.
    
    * NIFI-13670: Not setting valueRemoved when deleting a parameter.
    
    * NIFI-13670: Marking field as dirty following removal of sensitive value 
set text.
---
 .../parameter-context-listing.effects.ts           |  18 +-
 .../edit-parameter-context.component.ts            |   5 +-
 .../parameter-table/parameter-table.component.html |  78 ++++----
 .../parameter-table/parameter-table.component.ts   | 219 +++++++++++++++------
 .../apps/nifi/src/app/state/shared/index.ts        |   1 +
 .../edit-parameter-dialog.component.html           |   7 +-
 .../edit-parameter-dialog.component.ts             |  84 ++++++--
 .../editors/nf-editor/nf-editor.component.html     |   2 +-
 .../editors/nf-editor/nf-editor.component.ts       |  23 ++-
 .../libs/shared/src/assets/styles/_app.scss        |   4 +-
 10 files changed, 310 insertions(+), 131 deletions(-)

diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/state/parameter-context-listing/parameter-context-listing.effects.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/state/parameter-context-listing/parameter-context-listing.effects.ts
index 6da70b863d..4f5daaf489 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/state/parameter-context-listing/parameter-context-listing.effects.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/state/parameter-context-listing/parameter-context-listing.effects.ts
@@ -113,7 +113,7 @@ export class ParameterContextListingEffects {
 
                     dialogReference.componentInstance.createNewParameter = (
                         existingParameters: string[]
-                    ): Observable<Parameter> => {
+                    ): Observable<EditParameterResponse> => {
                         const dialogRequest: EditParameterRequest = { 
existingParameters };
                         const newParameterDialogReference = 
this.dialog.open(EditParameterDialog, {
                             ...MEDIUM_DIALOG,
@@ -128,13 +128,15 @@ export class ParameterContextListingEffects {
                                 newParameterDialogReference.close();
 
                                 return {
-                                    ...dialogResponse.parameter
+                                    ...dialogResponse
                                 };
                             })
                         );
                     };
 
-                    dialogReference.componentInstance.editParameter = 
(parameter: Parameter): Observable<Parameter> => {
+                    dialogReference.componentInstance.editParameter = (
+                        parameter: Parameter
+                    ): Observable<EditParameterResponse> => {
                         const dialogRequest: EditParameterRequest = {
                             parameter: {
                                 ...parameter
@@ -153,7 +155,7 @@ export class ParameterContextListingEffects {
                                 editParameterDialogReference.close();
 
                                 return {
-                                    ...dialogResponse.parameter
+                                    ...dialogResponse
                                 };
                             })
                         );
@@ -317,7 +319,7 @@ export class ParameterContextListingEffects {
 
                     editDialogReference.componentInstance.createNewParameter = 
(
                         existingParameters: string[]
-                    ): Observable<Parameter> => {
+                    ): Observable<EditParameterResponse> => {
                         const dialogRequest: EditParameterRequest = { 
existingParameters };
                         const newParameterDialogReference = 
this.dialog.open(EditParameterDialog, {
                             ...MEDIUM_DIALOG,
@@ -332,7 +334,7 @@ export class ParameterContextListingEffects {
                                 newParameterDialogReference.close();
 
                                 return {
-                                    ...dialogResponse.parameter
+                                    ...dialogResponse
                                 };
                             })
                         );
@@ -340,7 +342,7 @@ export class ParameterContextListingEffects {
 
                     editDialogReference.componentInstance.editParameter = (
                         parameter: Parameter
-                    ): Observable<Parameter> => {
+                    ): Observable<EditParameterResponse> => {
                         const dialogRequest: EditParameterRequest = {
                             parameter: {
                                 ...parameter
@@ -359,7 +361,7 @@ export class ParameterContextListingEffects {
                                 editParameterDialogReference.close();
 
                                 return {
-                                    ...dialogResponse.parameter
+                                    ...dialogResponse
                                 };
                             })
                         );
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/edit-parameter-context/edit-parameter-context.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/edit-parameter-context/edit-parameter-context.component.ts
index 328e35154f..fe3944b7bc 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/edit-parameter-context/edit-parameter-context.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/edit-parameter-context/edit-parameter-context.component.ts
@@ -31,6 +31,7 @@ import { NifiSpinnerDirective } from 
'../../../../../ui/common/spinner/nifi-spin
 import { Client } from '../../../../../service/client.service';
 import { ParameterTable } from '../parameter-table/parameter-table.component';
 import {
+    EditParameterResponse,
     Parameter,
     ParameterContextEntity,
     ParameterContextUpdateRequestEntity,
@@ -73,8 +74,8 @@ import { NiFiCommon, TextTip, NifiTooltipDirective } from 
'@nifi/shared';
     styleUrls: ['./edit-parameter-context.component.scss']
 })
 export class EditParameterContext extends TabbedDialog {
-    @Input() createNewParameter!: (existingParameters: string[]) => 
Observable<Parameter>;
-    @Input() editParameter!: (parameter: Parameter) => Observable<Parameter>;
+    @Input() createNewParameter!: (existingParameters: string[]) => 
Observable<EditParameterResponse>;
+    @Input() editParameter!: (parameter: Parameter) => 
Observable<EditParameterResponse>;
     @Input() updateRequest!: Observable<ParameterContextUpdateRequestEntity | 
null>;
     @Input() availableParameterContexts$!: 
Observable<ParameterContextEntity[]>;
     @Input() saving$!: Observable<boolean>;
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html
index 0c9b92358d..40aab1b589 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.html
@@ -41,15 +41,15 @@
                             <div class="flex justify-between items-center">
                                 <div
                                     class="whitespace-nowrap overflow-hidden 
text-ellipsis leading-normal"
-                                    [title]="item.entity.parameter.name">
-                                    {{ item.entity.parameter.name }}
+                                    
[title]="item.originalEntity.parameter.name">
+                                    {{ item.originalEntity.parameter.name }}
                                 </div>
                                 @if (hasDescription(item)) {
                                     <i
                                         class="fa fa-info-circle primary-color"
                                         nifiTooltip
                                         [tooltipComponentType]="TextTip"
-                                        
[tooltipInputData]="item.entity.parameter.description"
+                                        
[tooltipInputData]="getDescription(item)"
                                         [delayClose]="false"></i>
                                 }
                                 @if (canOverride(item)) {
@@ -68,42 +68,46 @@
                     <ng-container matColumnDef="value">
                         <th mat-header-cell *matHeaderCellDef>Value</th>
                         <td mat-cell *matCellDef="let item">
-                            @if (isNull(item.entity.parameter.value)) {
-                                <div class="unset surface-color">No value 
set</div>
-                            } @else {
-                                <ng-container
-                                    *ngTemplateOutlet="
-                                        isSensitiveParameter(item) ? sensitive 
: nonSensitive;
-                                        context: { $implicit: 
item.entity.parameter.value }
-                                    "></ng-container>
-                                <ng-template #sensitive>
-                                    <div class="sensitive 
surface-color">Sensitive value set</div>
-                                </ng-template>
-                                <ng-template #nonSensitive let-value>
+                            <ng-container
+                                *ngTemplateOutlet="renderValue; context: { 
$implicit: getValue(item) }"></ng-container>
+                            <ng-template #renderValue let-parameterValue>
+                                @if (isNull(parameterValue)) {
+                                    <div class="unset surface-color">No value 
set</div>
+                                } @else {
                                     <ng-container
                                         *ngTemplateOutlet="
-                                            isEmptyString(value) ? blank : 
nonBlank;
-                                            context: { $implicit: value }
+                                            isSensitiveParameter(item) ? 
sensitive : nonSensitive;
+                                            context: { $implicit: 
parameterValue }
                                         "></ng-container>
-                                </ng-template>
-                                <ng-template #blank>
-                                    <div class="empty surface-color">Empty 
string set</div>
-                                </ng-template>
-                                <ng-template #nonBlank let-value>
-                                    <div class="flex justify-between 
items-center">
-                                        <div class="whitespace-nowrap 
overflow-hidden text-ellipsis">
-                                            {{ value }}
+                                    <ng-template #sensitive>
+                                        <div class="sensitive 
surface-color">Sensitive value set</div>
+                                    </ng-template>
+                                    <ng-template #nonSensitive let-value>
+                                        <ng-container
+                                            *ngTemplateOutlet="
+                                                isEmptyString(value) ? blank : 
nonBlank;
+                                                context: { $implicit: value }
+                                            "></ng-container>
+                                    </ng-template>
+                                    <ng-template #blank>
+                                        <div class="empty surface-color">Empty 
string set</div>
+                                    </ng-template>
+                                    <ng-template #nonBlank let-value>
+                                        <div class="flex justify-between 
items-center">
+                                            <div class="whitespace-nowrap 
overflow-hidden text-ellipsis">
+                                                {{ value }}
+                                            </div>
+                                            @if (hasExtraWhitespace(value)) {
+                                                <div
+                                                    class="fa fa-info-circle 
primary-color"
+                                                    nifiTooltip
+                                                    
[tooltipComponentType]="TextTip"
+                                                    tooltipInputData="The 
specified value contains leading and/or trailing whitespace character(s). This 
could produce unexpected results if it was not intentional."></div>
+                                            }
                                         </div>
-                                        @if (hasExtraWhitespace(value)) {
-                                            <div
-                                                class="fa fa-info-circle 
primary-color"
-                                                nifiTooltip
-                                                
[tooltipComponentType]="TextTip"
-                                                tooltipInputData="The 
specified value contains leading and/or trailing whitespace character(s). This 
could produce unexpected results if it was not intentional."></div>
-                                        }
-                                    </div>
-                                </ng-template>
-                            }
+                                    </ng-template>
+                                }
+                            </ng-template>
                         </td>
                     </ng-container>
 
@@ -171,7 +175,7 @@
             <div>Parameter</div>
             <div class="accent-color font-medium">
                 @if (selectedItem) {
-                    <span>{{ selectedItem.entity.parameter.name }}</span>
+                    <span>{{ selectedItem.originalEntity.parameter.name 
}}</span>
                 } @else {
                     <span class="unset surface-color">None</span>
                 }
@@ -191,7 +195,7 @@
                     @if (selectedItem) {
                         <parameter-references
                             [parameterReferences]="
-                                
selectedItem.entity.parameter.referencingComponents
+                                
selectedItem.originalEntity.parameter.referencingComponents
                             "></parameter-references>
                     }
                 </div>
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts
index 70f10fbb30..1d1c5fe01a 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-table/parameter-table.component.ts
@@ -23,7 +23,7 @@ import { MatTableDataSource, MatTableModule } from 
'@angular/material/table';
 import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
 import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
 import { RouterLink } from '@angular/router';
-import { Parameter, ParameterEntity } from '../../../../../state/shared';
+import { EditParameterResponse, Parameter, ParameterEntity } from 
'../../../../../state/shared';
 import { NifiTooltipDirective, NiFiCommon, TextTip } from '@nifi/shared';
 import { Observable, take } from 'rxjs';
 import { ParameterReferences } from 
'../../../../../ui/common/parameter-references/parameter-references.component';
@@ -37,7 +37,8 @@ export interface ParameterItem {
     deleted: boolean;
     dirty: boolean;
     added: boolean;
-    entity: ParameterEntity;
+    originalEntity: ParameterEntity; // either new or existing from server
+    updatedEntity?: ParameterEntity;
 }
 
 @Component({
@@ -70,8 +71,8 @@ export interface ParameterItem {
     ]
 })
 export class ParameterTable implements AfterViewInit, ControlValueAccessor {
-    @Input() createNewParameter!: (existingParameters: string[]) => 
Observable<Parameter>;
-    @Input() editParameter!: (parameter: Parameter) => Observable<Parameter>;
+    @Input() createNewParameter!: (existingParameters: string[]) => 
Observable<EditParameterResponse>;
+    @Input() editParameter!: (parameter: Parameter) => 
Observable<EditParameterResponse>;
     @Input() canAddParameters = true;
 
     protected readonly TextTip = TextTip;
@@ -128,10 +129,11 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
                 deleted: false,
                 added: false,
                 dirty: false,
-                entity: {
+                originalEntity: {
                     ...entity,
                     parameter: {
-                        ...entity.parameter
+                        ...entity.parameter,
+                        value: entity.parameter.value === undefined ? null : 
entity.parameter.value
                     }
                 }
             };
@@ -161,7 +163,10 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
             let retVal = 0;
             switch (sort.active) {
                 case 'name':
-                    retVal = 
this.nifiCommon.compareString(a.entity.parameter.name, b.entity.parameter.name);
+                    retVal = this.nifiCommon.compareString(
+                        a.originalEntity.parameter.name,
+                        b.originalEntity.parameter.name
+                    );
                     break;
                 default:
                     return 0;
@@ -176,25 +181,27 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
         // unmarked for deletion if the user chooses to enter the same name
         const existingParameters: string[] = this.dataSource.data
             .filter((item) => !item.deleted)
-            .filter((item) => !item.entity.parameter.inherited)
-            .map((item) => item.entity.parameter.name);
+            .filter((item) => !item.originalEntity.parameter.inherited)
+            .map((item) => item.originalEntity.parameter.name);
 
         this.createNewParameter(existingParameters)
             .pipe(take(1))
-            .subscribe((parameter) => {
+            .subscribe((response: EditParameterResponse) => {
+                const parameter: Parameter = response.parameter;
+
                 const currentParameterItems: ParameterItem[] = 
this.dataSource.data;
 
                 // identify if a parameter with the same name already exists 
(must have been marked
                 // for deletion already)
                 const item: ParameterItem | undefined = 
currentParameterItems.find(
-                    (item) => item.entity.parameter.name === parameter.name
+                    (item) => item.originalEntity.parameter.name === 
parameter.name
                 );
 
                 if (item) {
                     // if the item is added that means it hasn't been saved 
yet. in this case, we
                     // can simply update the existing parameter. if the item 
has been saved, and the
                     // sensitivity has changed, the user must apply the 
changes first.
-                    if (!item.added && item.entity.parameter.sensitive !== 
parameter.sensitive) {
+                    if (!item.added && item.originalEntity.parameter.sensitive 
!== parameter.sensitive) {
                         this.store.dispatch(
                             showOkDialog({
                                 title: 'Parameter Exists',
@@ -208,15 +215,21 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
                     // update the existing item
                     item.deleted = false;
                     item.dirty = true;
-                    item.entity.parameter = {
-                        ...parameter
-                    };
+
+                    // since the item is either new or the sensitivity is the 
same, accept the sensitivity
+                    // from the response to be set on the entities
+                    item.originalEntity.parameter.sensitive = 
parameter.sensitive;
+                    if (item.updatedEntity) {
+                        item.updatedEntity.parameter.sensitive = 
parameter.sensitive;
+                    }
+
+                    this.applyParameterEdit(item, response);
                 } else {
                     const newItem: ParameterItem = {
                         deleted: false,
                         added: true,
                         dirty: true,
-                        entity: {
+                        originalEntity: {
                             canWrite: true,
                             parameter: {
                                 ...parameter
@@ -232,11 +245,36 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
     }
 
     hasDescription(item: ParameterItem): boolean {
-        return !this.nifiCommon.isBlank(item.entity.parameter.description);
+        return !this.nifiCommon.isBlank(this.getDescription(item));
+    }
+
+    getDescription(item: ParameterItem): string {
+        if (item.updatedEntity) {
+            return item.updatedEntity.parameter.description;
+        } else {
+            return item.originalEntity.parameter.description;
+        }
     }
 
     isSensitiveParameter(item: ParameterItem): boolean {
-        return item.entity.parameter.sensitive;
+        return item.originalEntity.parameter.sensitive;
+    }
+
+    getValue(item: ParameterItem): string | null {
+        if (item.updatedEntity) {
+            if (item.updatedEntity.parameter.valueRemoved) {
+                return null;
+            } else {
+                // if there is an updated value use that, otherwise the 
original
+                if (item.updatedEntity.parameter.value !== null) {
+                    return item.updatedEntity.parameter.value;
+                } else {
+                    return item.originalEntity.parameter.value;
+                }
+            }
+        } else {
+            return item.originalEntity.parameter.value;
+        }
     }
 
     isNull(value: string): boolean {
@@ -253,35 +291,37 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
 
     canGoToParameter(item: ParameterItem): boolean {
         return (
-            item.entity.parameter.inherited === true &&
-            item.entity.parameter.parameterContext?.permissions.canRead == true
+            item.originalEntity.parameter.inherited === true &&
+            
item.originalEntity.parameter.parameterContext?.permissions.canRead == true
         );
     }
 
     getParameterLink(item: ParameterItem): string[] {
-        if (item.entity.parameter.parameterContext) {
+        if (item.originalEntity.parameter.parameterContext) {
             // TODO - support routing directly to a parameter
-            return ['/parameter-contexts', 
item.entity.parameter.parameterContext.id, 'edit'];
+            return ['/parameter-contexts', 
item.originalEntity.parameter.parameterContext.id, 'edit'];
         }
         return [];
     }
 
     canOverride(item: ParameterItem): boolean {
-        return item.entity.parameter.inherited === true;
+        return item.originalEntity.parameter.inherited === true;
     }
 
     overrideParameter(item: ParameterItem): void {
         const overriddenParameter: Parameter = {
-            ...item.entity.parameter,
+            ...item.originalEntity.parameter,
             value: null
         };
 
         this.editParameter(overriddenParameter)
             .pipe(take(1))
-            .subscribe((parameter) => {
+            .subscribe((response) => {
                 item.dirty = true;
-                item.entity.parameter = {
-                    ...parameter
+                item.updatedEntity = {
+                    parameter: {
+                        ...response.parameter
+                    }
                 };
 
                 this.handleChanged();
@@ -289,46 +329,93 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
     }
 
     canEdit(item: ParameterItem): boolean {
-        const canWrite: boolean = item.entity.canWrite == true;
-        const provided: boolean = item.entity.parameter.provided == true;
-        const inherited: boolean = item.entity.parameter.inherited == true;
+        const canWrite: boolean = item.originalEntity.canWrite == true;
+        const provided: boolean = item.originalEntity.parameter.provided == 
true;
+        const inherited: boolean = item.originalEntity.parameter.inherited == 
true;
         return canWrite && !provided && !inherited;
     }
 
     editClicked(item: ParameterItem): void {
-        this.editParameter(item.entity.parameter)
+        const parameterToEdit: Parameter = {
+            name: item.originalEntity.parameter.name,
+            sensitive: item.originalEntity.parameter.sensitive,
+            description: this.getDescription(item),
+            value: this.getValue(item)
+        };
+
+        this.editParameter(parameterToEdit)
             .pipe(take(1))
-            .subscribe((parameter) => {
-                const valueChanged: boolean = item.entity.parameter.value != 
parameter.value;
-                const descriptionChanged: boolean = 
item.entity.parameter.description != parameter.description;
-                const valueRemovedChanged: boolean = 
item.entity.parameter.valueRemoved != parameter.valueRemoved;
-
-                if (valueChanged || descriptionChanged || valueRemovedChanged) 
{
-                    item.entity.parameter.value = parameter.value;
-                    item.entity.parameter.description = parameter.description;
-                    item.entity.parameter.valueRemoved = 
parameter.valueRemoved;
-                    item.dirty = true;
+            .subscribe((response) => {
+                this.applyParameterEdit(item, response);
+            });
+    }
 
-                    if (valueChanged) {
-                        item.entity.parameter.referencedAssets = undefined;
-                    }
+    private applyParameterEdit(item: ParameterItem, response: 
EditParameterResponse) {
+        const parameter: Parameter = response.parameter;
 
-                    this.handleChanged();
+        // initialize the updated entity if this is the first time this has 
been edited
+        if (!item.updatedEntity) {
+            item.updatedEntity = {
+                parameter: {
+                    ...item.originalEntity.parameter,
+                    value: null
                 }
-            });
+            };
+        }
+
+        let hasChanged: boolean = response.valueChanged;
+
+        if (response.valueChanged) {
+            item.updatedEntity.parameter.value = parameter.value;
+
+            // a value has been specified so record it in the updated entity
+            if (parameter.value !== null) {
+                item.updatedEntity.parameter.valueRemoved = undefined;
+                item.updatedEntity.parameter.referencedAssets = undefined;
+            } else {
+                // the value is null, this means that the value should be 
unchanged
+                // or that the value has been removed. if removed we should
+                // clear our the value.
+                if (parameter.valueRemoved === true) {
+                    item.updatedEntity.parameter.value = null;
+                    item.updatedEntity.parameter.valueRemoved = true;
+                    item.updatedEntity.parameter.referencedAssets = undefined;
+                }
+            }
+        }
+
+        if (item.updatedEntity.parameter.description !== 
parameter.description) {
+            hasChanged = true;
+            item.updatedEntity.parameter.description = parameter.description;
+        }
+
+        // if any aspect of the parameter has changed, mark it dirty and 
trigger handle changed
+        if (hasChanged) {
+            item.dirty = true;
+            this.handleChanged();
+        }
     }
 
     canDelete(item: ParameterItem): boolean {
-        const canWrite: boolean = item.entity.canWrite == true;
-        const provided: boolean = item.entity.parameter.provided == true;
-        const inherited: boolean = item.entity.parameter.inherited == true;
+        const canWrite: boolean = item.originalEntity.canWrite == true;
+        const provided: boolean = item.originalEntity.parameter.provided == 
true;
+        const inherited: boolean = item.originalEntity.parameter.inherited == 
true;
         return canWrite && !provided && !inherited;
     }
 
     deleteClicked(item: ParameterItem): void {
         if (!item.deleted) {
-            item.entity.parameter.value = null;
-            item.entity.parameter.valueRemoved = true;
+            if (!item.updatedEntity) {
+                item.updatedEntity = {
+                    parameter: {
+                        ...item.originalEntity.parameter
+                    }
+                };
+            }
+
+            item.updatedEntity.parameter.value = null;
+            item.updatedEntity.parameter.referencedAssets = undefined;
+
             item.deleted = true;
             item.dirty = true;
             this.selectParameter(null);
@@ -360,20 +447,34 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
             .filter((item) => !(item.added && item.deleted))
             .map((item) => {
                 if (item.deleted) {
+                    // item is deleted
+                    return {
+                        parameter: {
+                            name: item.originalEntity.parameter.name
+                        }
+                    };
+                } else if (item.updatedEntity) {
+                    // item has been edited
                     return {
                         parameter: {
-                            name: item.entity.parameter.name
+                            name: item.originalEntity.parameter.name,
+                            sensitive: item.originalEntity.parameter.sensitive,
+                            description: 
item.updatedEntity.parameter.description,
+                            value: item.updatedEntity.parameter.value,
+                            valueRemoved: 
item.updatedEntity.parameter.valueRemoved,
+                            referencedAssets: 
item.updatedEntity.parameter.referencedAssets
                         }
                     };
                 } else {
+                    // item is added (but not subsequently edited)
                     return {
                         parameter: {
-                            name: item.entity.parameter.name,
-                            sensitive: item.entity.parameter.sensitive,
-                            description: item.entity.parameter.description,
-                            value: item.entity.parameter.value,
-                            valueRemoved: item.entity.parameter.valueRemoved,
-                            referencedAssets: 
item.entity.parameter.referencedAssets
+                            name: item.originalEntity.parameter.name,
+                            sensitive: item.originalEntity.parameter.sensitive,
+                            description: 
item.originalEntity.parameter.description,
+                            value: item.originalEntity.parameter.value,
+                            valueRemoved: 
item.originalEntity.parameter.valueRemoved,
+                            referencedAssets: 
item.originalEntity.parameter.referencedAssets
                         }
                     };
                 }
@@ -386,7 +487,7 @@ export class ParameterTable implements AfterViewInit, 
ControlValueAccessor {
 
     isSelected(item: ParameterItem): boolean {
         if (this.selectedItem) {
-            return item.entity.parameter.name == 
this.selectedItem.entity.parameter.name;
+            return item.originalEntity.parameter.name == 
this.selectedItem.originalEntity.parameter.name;
         }
         return false;
     }
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/state/shared/index.ts 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/state/shared/index.ts
index 6b5522572f..17b9126cca 100644
--- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/state/shared/index.ts
+++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/state/shared/index.ts
@@ -50,6 +50,7 @@ export interface EditParameterRequest {
 
 export interface EditParameterResponse {
     parameter: Parameter;
+    valueChanged: boolean;
 }
 
 export interface AdvancedUiParams {
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/edit-parameter-dialog/edit-parameter-dialog.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/edit-parameter-dialog/edit-parameter-dialog.component.html
index c731da34e2..a828469ee9 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/edit-parameter-dialog/edit-parameter-dialog.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/edit-parameter-dialog/edit-parameter-dialog.component.html
@@ -37,7 +37,12 @@
                         [tooltipComponentType]="TextTip"
                         tooltipInputData="Parameter values do not support 
Expression Language or embedded parameter references."></i>
                 </mat-label>
-                <textarea matInput formControlName="value" 
type="text"></textarea>
+                <textarea
+                    matInput
+                    formControlName="value"
+                    type="text"
+                    [class.sensitive]="showSensitiveHelperText"
+                    (keydown)="clearSensitiveHelperText()"></textarea>
             </mat-form-field>
         </div>
         <div class="-mt-4 mb-4">
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/edit-parameter-dialog/edit-parameter-dialog.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/edit-parameter-dialog/edit-parameter-dialog.component.ts
index 171879375f..6a48555751 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/edit-parameter-dialog/edit-parameter-dialog.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/edit-parameter-dialog/edit-parameter-dialog.component.ts
@@ -17,7 +17,7 @@
 
 import { Component, EventEmitter, Inject, Input, Output } from '@angular/core';
 import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
-import { EditParameterRequest, EditParameterResponse, Parameter, 
ReferencedAsset } from '../../../state/shared';
+import { EditParameterRequest, EditParameterResponse, Parameter } from 
'../../../state/shared';
 import { MatButtonModule } from '@angular/material/button';
 import {
     AbstractControl,
@@ -67,6 +67,8 @@ export class EditParameterDialog extends CloseOnEscapeDialog {
     sensitive: FormControl;
     editParameterForm: FormGroup;
     isNew: boolean;
+    showSensitiveHelperText: boolean = false;
+    valueInputTriggered: boolean = false;
 
     private originalParameter: Parameter | undefined = undefined;
 
@@ -81,10 +83,14 @@ export class EditParameterDialog extends 
CloseOnEscapeDialog {
         const parameter: Parameter | undefined = request.parameter;
         this.originalParameter = parameter;
 
+        let value: string | null;
+
         const validators: any[] = [Validators.required];
         if (request.existingParameters) {
             this.isNew = true;
 
+            value = parameter ? parameter.value : null;
+
             // since there were existing parameters in the request, add the 
existing parameters validator because
             // parameters names must be unique
             
validators.push(this.existingParameterValidator(request.existingParameters));
@@ -100,20 +106,25 @@ export class EditParameterDialog extends 
CloseOnEscapeDialog {
         } else {
             this.isNew = false;
 
+            value = parameter ? parameter.value : null;
+
+            const sensitive = parameter ? parameter.sensitive : false;
+            if (sensitive && value !== null) {
+                value = 'Sensitive value set';
+                this.showSensitiveHelperText = true;
+            }
+
             // without existingParameters, we are editing an existing 
parameter. in this case the name and sensitivity cannot be modified
             this.name = new FormControl(
                 { value: parameter ? parameter.name : '', disabled: true },
                 Validators.required
             );
-            this.sensitive = new FormControl(
-                { value: parameter ? parameter.sensitive : false, disabled: 
true },
-                Validators.required
-            );
+            this.sensitive = new FormControl({ value: sensitive, disabled: 
true }, Validators.required);
         }
 
         this.editParameterForm = this.formBuilder.group({
             name: this.name,
-            value: new FormControl(parameter ? parameter.value : null),
+            value: new FormControl(value),
             empty: new FormControl(parameter ? parameter.value == '' : false),
             sensitive: this.sensitive,
             description: new FormControl(parameter ? parameter.description : 
'')
@@ -146,12 +157,23 @@ export class EditParameterDialog extends 
CloseOnEscapeDialog {
         return this.name.hasError('existingParameter') ? 'A parameter with 
this name already exists.' : '';
     }
 
+    clearSensitiveHelperText(): void {
+        if (this.showSensitiveHelperText) {
+            this.editParameterForm.get('value')?.setValue('');
+            this.showSensitiveHelperText = false;
+        }
+
+        this.valueInputTriggered = true;
+    }
+
     setEmptyStringChanged(): void {
         const emptyStringChecked: AbstractControl | null = 
this.editParameterForm.get('empty');
         if (emptyStringChecked) {
             if (emptyStringChecked.value) {
                 this.editParameterForm.get('value')?.setValue('');
                 this.editParameterForm.get('value')?.disable();
+
+                this.valueInputTriggered = true;
             } else {
                 this.editParameterForm.get('value')?.enable();
             }
@@ -162,24 +184,50 @@ export class EditParameterDialog extends 
CloseOnEscapeDialog {
         this.cancel.next();
     }
 
+    private valueChanged(enteredValue: string | null): boolean {
+        let valueChanged = true;
+        if (this.originalParameter) {
+            if (this.originalParameter.sensitive) {
+                valueChanged = this.valueInputTriggered;
+            } else {
+                valueChanged = enteredValue !== this.originalParameter.value;
+            }
+        }
+        return valueChanged;
+    }
+
     okClicked(): void {
-        const value: string = this.editParameterForm.get('value')?.value;
+        const value: string | null = 
this.editParameterForm.get('value')?.value;
         const empty: boolean = this.editParameterForm.get('empty')?.value;
-        let referencedAssets: ReferencedAsset[] | undefined = undefined;
 
-        if (this.originalParameter) {
-            referencedAssets = this.originalParameter.referencedAssets;
+        const parameter: Parameter = {
+            name: this.editParameterForm.get('name')?.value,
+            value: null,
+            sensitive: this.editParameterForm.get('sensitive')?.value,
+            description: this.editParameterForm.get('description')?.value
+        };
+
+        // update the parameter value
+        let valueChanged = this.valueChanged(value);
+        if (valueChanged) {
+            parameter.value = value;
         }
 
-        this.editParameter.next({
-            parameter: {
-                name: this.editParameterForm.get('name')?.value,
-                value: value === '' && !empty ? null : value,
-                valueRemoved: value === '' && !empty,
-                sensitive: this.editParameterForm.get('sensitive')?.value,
-                description: this.editParameterForm.get('description')?.value,
-                referencedAssets
+        // indicate if the value has been removed
+        const valueRemoved = value === '' && !empty;
+        if (valueRemoved) {
+            valueChanged = true;
+            parameter.value = null;
+
+            // if this is a new parameter there is no need to indicate that 
the value is removed
+            if (!this.isNew) {
+                parameter.valueRemoved = true;
             }
+        }
+
+        this.editParameter.next({
+            parameter,
+            valueChanged
         });
     }
 
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.html
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.html
index b46575b993..be1026dfc9 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.html
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.html
@@ -47,7 +47,7 @@
                 </ng-template>
             </div>
             <div class="flex flex-col gap-y-0.5 flex-1">
-                <div class="nf-editor flex-1" [class.blank]="blank">
+                <div class="nf-editor flex-1" [class.blank]="blank" 
[class.sensitive]="showSensitiveHelperText">
                     <ngx-codemirror
                         [options]="getOptions()"
                         formControlName="value"
diff --git 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts
 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts
index 1a396e2a3f..36dd4e7dfa 100644
--- 
a/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts
+++ 
b/nifi-frontend/src/main/frontend/apps/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts
@@ -55,7 +55,13 @@ import { Editor } from 'codemirror';
 })
 export class NfEditor implements OnDestroy {
     @Input() set item(item: PropertyItem) {
-        this.nfEditorForm.get('value')?.setValue(item.value);
+        if (item.descriptor.sensitive && item.value !== null) {
+            this.nfEditorForm.get('value')?.setValue('Sensitive value set');
+            this.showSensitiveHelperText = true;
+        } else {
+            this.nfEditorForm.get('value')?.setValue(item.value);
+        }
+
         if (item.descriptor.required) {
             this.nfEditorForm.get('value')?.addValidators(Validators.required);
         } else {
@@ -67,7 +73,6 @@ export class NfEditor implements OnDestroy {
         this.setEmptyStringChanged();
 
         this.supportsEl = item.descriptor.supportsEl;
-        this.sensitive = item.descriptor.sensitive;
         this.mode = this.supportsEl ? this.nfel.getLanguageId() : 
this.nfpr.getLanguageId();
 
         this.itemSet = true;
@@ -93,7 +98,7 @@ export class NfEditor implements OnDestroy {
     getParametersSet = false;
 
     nfEditorForm: FormGroup;
-    sensitive = false;
+    showSensitiveHelperText = false;
     supportsEl = false;
     supportsParameters = false;
     blank = false;
@@ -124,6 +129,18 @@ export class NfEditor implements OnDestroy {
             this.editor.execCommand('selectAll');
         }
 
+        if (this.showSensitiveHelperText) {
+            const clearSensitiveHelperText = () => {
+                if (this.showSensitiveHelperText) {
+                    this.nfEditorForm.get('value')?.setValue('');
+                    this.nfEditorForm.get('value')?.markAsDirty();
+                    this.showSensitiveHelperText = false;
+                }
+            };
+
+            this.editor.on('keydown', clearSensitiveHelperText);
+        }
+
         // disabling of the input through the form isn't supported until 
codemirror
         // has loaded so we must disable again if the value is an empty string
         if (this.nfEditorForm.get('setEmptyString')?.value) {
diff --git 
a/nifi-frontend/src/main/frontend/libs/shared/src/assets/styles/_app.scss 
b/nifi-frontend/src/main/frontend/libs/shared/src/assets/styles/_app.scss
index 5e6c914bde..886458fcaf 100644
--- a/nifi-frontend/src/main/frontend/libs/shared/src/assets/styles/_app.scss
+++ b/nifi-frontend/src/main/frontend/libs/shared/src/assets/styles/_app.scss
@@ -56,8 +56,8 @@
     .unset,
     .sensitive {
         font-weight: normal !important;
-        font-style: italic;
-        opacity: 0.8;
+        font-style: italic !important;
+        opacity: 0.8 !important;
     }
 
     .context-logo {


Reply via email to