This is an automated email from the ASF dual-hosted git repository.
rfellows 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 ebfb5bc12e NIFI-12401: Allow combo editor to reference parameters
(#8068)
ebfb5bc12e is described below
commit ebfb5bc12e94563cedeb042a6571e537f99ca3d6
Author: Matt Gilman <[email protected]>
AuthorDate: Wed Nov 29 12:26:25 2023 -0500
NIFI-12401: Allow combo editor to reference parameters (#8068)
* NIFI-12401:
- Allow combo editor to reference parameters.
* NIFI-12401:
- Addressing review feedback.
- Handling corner cases where there is no parameter context and where there
are no parameters in a bound parameter context.
* NIFI-12401:
- Fixing formatting issues.
This closes #8068
---
.../nifi-web-frontend/src/main/nifi/karma.conf.js | 65 +++---
.../nifi-web-frontend/src/main/nifi/package.json | 2 +-
.../canvas/ui/header/search/search.component.html | 2 +-
.../src/main/nifi/src/app/state/shared/index.ts | 6 +-
.../combo-editor/combo-editor.component.html | 38 +++-
.../combo-editor/combo-editor.component.spec.ts | 160 ++++++++++++++-
.../editors/combo-editor/combo-editor.component.ts | 226 +++++++++++++++++----
.../editors/nf-editor/nf-editor.component.spec.ts | 77 ++++++-
.../editors/nf-editor/nf-editor.component.ts | 13 +-
.../property-table/property-table.component.html | 1 +
.../property-table/property-table.component.ts | 6 +-
11 files changed, 495 insertions(+), 101 deletions(-)
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/karma.conf.js
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/karma.conf.js
index 5ffa7d98cb..568ec857ed 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/karma.conf.js
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/karma.conf.js
@@ -19,38 +19,35 @@
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
- config.set({
- basePath: '',
- frameworks: ['jasmine', '@angular-devkit/build-angular'],
- plugins: [
- require('karma-jasmine'),
- require('karma-chrome-launcher'),
- require('karma-jasmine-html-reporter'),
- require('karma-coverage'),
- require('@angular-devkit/build-angular/plugins/karma')
- ],
- client: {
- jasmine: {
- // you can add configuration options for Jasmine here
- // the possible options are listed at
https://jasmine.github.io/api/edge/Configuration.html
- // for example, you can disable the random execution with `random:
false`
- // or set a specific seed with `seed: 4321`
- },
- clearContext: false // leave Jasmine Spec Runner output visible in
browser
- },
- jasmineHtmlReporter: {
- suppressAll: true // removes the duplicated traces
- },
- coverageReporter: {
- dir: require('path').join(__dirname, './coverage/nifi'),
- subdir: '.',
- reporters: [
- { type: 'html' },
- { type: 'text-summary' }
- ]
- },
- reporters: ['progress', 'kjhtml'],
- browsers: ['ChromeHeadless'],
- restartOnFileChange: true
- });
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-coverage'),
+ require('@angular-devkit/build-angular/plugins/karma')
+ ],
+ client: {
+ jasmine: {
+ // you can add configuration options for Jasmine here
+ // the possible options are listed at
https://jasmine.github.io/api/edge/Configuration.html
+ // for example, you can disable the random execution with
`random: false`
+ // or set a specific seed with `seed: 4321`
+ },
+ clearContext: false // leave Jasmine Spec Runner output visible in
browser
+ },
+ jasmineHtmlReporter: {
+ suppressAll: true // removes the duplicated traces
+ },
+ coverageReporter: {
+ dir: require('path').join(__dirname, './coverage/nifi'),
+ subdir: '.',
+ reporters: [{ type: 'html' }, { type: 'text-summary' }]
+ },
+ reporters: ['progress', 'kjhtml'],
+ browsers: ['ChromeHeadless'],
+ restartOnFileChange: true
+ });
};
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/package.json
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/package.json
index 7f54077afd..1f33078257 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/package.json
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/package.json
@@ -43,11 +43,11 @@
"@angular-devkit/build-angular": "^16.2.0",
"@angular/cli": "~16.2.0",
"@angular/compiler-cli": "^16.2.0",
+ "@types/codemirror": "^5.60.13",
"@types/d3": "^7.4.0",
"@types/humanize-duration": "^3.27.1",
"@types/jasmine": "~4.3.0",
"@types/webfontloader": "^1.6.35",
- "@types/codemirror": "^5.60.13",
"autoprefixer": "^10.4.15",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/canvas/ui/header/search/search.component.html
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/canvas/ui/header/search/search.component.html
index da09c7b8ce..4249a64961 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/canvas/ui/header/search/search.component.html
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/canvas/ui/header/search/search.component.html
@@ -190,7 +190,7 @@
</a>
<ng-template #resultLink>
<a [routerLink]="['/process-groups',
result.parentGroup.id, path, result.id]">
- {{ result.name }}
+ {{ result.name ? result.name : result.id }}
</a>
</ng-template>
</li>
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts
index 21f7077bc3..90d9490fa7 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/shared/index.ts
@@ -171,6 +171,8 @@ export interface ParameterContextReference {
export interface AffectedComponentEntity {
permissions: Permissions;
+ id: string;
+ revision: Revision;
bulletins: BulletinEntity[];
component: AffectedComponent;
processGroup: ProcessGroupName;
@@ -289,8 +291,8 @@ export interface PropertyDescriptor {
name: string;
displayName: string;
description: string;
- defaultValue: string;
- allowableValues: AllowableValueEntity[];
+ defaultValue?: string;
+ allowableValues?: AllowableValueEntity[];
required: boolean;
sensitive: boolean;
dynamic: boolean;
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.html
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.html
index dd28ec7df7..502d7693f4 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.html
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.html
@@ -24,7 +24,8 @@
formControlName="value"
[placeholder]="getComboPlaceholder()"
[panelClass]="'combo-panel'"
- (mousedown)="preventDrag($event)">
+ (mousedown)="preventDrag($event)"
+ (selectionChange)="allowableValueChanged($event.value)">
<ng-container *ngFor="let allowableValue of
allowableValues">
<ng-container *ngIf="allowableValue.description; else
noDescription">
<mat-option
@@ -49,6 +50,41 @@
</ng-container>
</mat-select>
</div>
+ <div *ngIf="showParameterAllowableValues">
+ <div *ngIf="!parametersLoaded; else showParameters">
+ <ngx-skeleton-loader count="1"></ngx-skeleton-loader>
+ </div>
+ <ng-template #showParameters>
+ <mat-select
+ class="combo"
+ formControlName="parameterReference"
+ [panelClass]="'combo-panel'"
+ (mousedown)="preventDrag($event)">
+ <ng-container *ngFor="let parameterAllowableValue of
parameterAllowableValues">
+ <ng-container
*ngIf="parameterAllowableValue.description; else noDescription">
+ <mat-option
+ [value]="parameterAllowableValue.id"
+ (mousedown)="preventDrag($event)"
+ nifiTooltip
+ [tooltipComponentType]="TextTip"
+
[tooltipInputData]="getAllowableValueOptionTipData(parameterAllowableValue)"
+ [delayClose]="false">
+ <span class="option-text"
[class.unset]="parameterAllowableValue.value == null">{{
+ parameterAllowableValue.displayName
+ }}</span>
+ </mat-option>
+ </ng-container>
+ <ng-template #noDescription>
+ <mat-option
[value]="parameterAllowableValue.id" (mousedown)="preventDrag($event)">
+ <span class="option-text"
[class.unset]="parameterAllowableValue.value == null">{{
+ parameterAllowableValue.displayName
+ }}</span>
+ </mat-option>
+ </ng-template>
+ </ng-container>
+ </mat-select>
+ </ng-template>
+ </div>
<div class="flex justify-end items-center gap-x-2">
<button
color="accent"
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.spec.ts
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.spec.ts
index 8397e0a0a4..a54496db20 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.spec.ts
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.spec.ts
@@ -18,19 +18,67 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComboEditor } from './combo-editor.component';
+import { PropertyItem } from '../../property-table.component';
+import { Parameter } from '../../../../../state/shared';
+import { of } from 'rxjs';
describe('ComboEditor', () => {
let component: ComboEditor;
let fixture: ComponentFixture<ComboEditor>;
+ let item: PropertyItem | null = null;
+ let parameters: Parameter[] = [
+ {
+ name: 'one',
+ description: 'Description for one.',
+ sensitive: false,
+ value: 'value',
+ provided: false,
+ referencingComponents: [],
+ parameterContext: {
+ id: '95d4f3d2-018b-1000-b7c7-b830c49a8026',
+ permissions: {
+ canRead: true,
+ canWrite: true
+ },
+ component: {
+ id: '95d4f3d2-018b-1000-b7c7-b830c49a8026',
+ name: 'params 1'
+ }
+ },
+ inherited: false
+ },
+ {
+ name: 'two',
+ description: 'Description for two.',
+ sensitive: false,
+ value: 'value',
+ provided: false,
+ referencingComponents: [],
+ parameterContext: {
+ id: '95d4f3d2-018b-1000-b7c7-b830c49a8026',
+ permissions: {
+ canRead: true,
+ canWrite: true
+ },
+ component: {
+ id: '95d4f3d2-018b-1000-b7c7-b830c49a8026',
+ name: 'params 1'
+ }
+ },
+ inherited: false
+ }
+ ];
+
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ComboEditor]
});
fixture = TestBed.createComponent(ComboEditor);
component = fixture.componentInstance;
- component.supportsParameters = false;
- component.item = {
+
+ // re-establish the item before each test execution
+ item = {
property: 'Destination',
value: 'flowfile-attribute',
descriptor: {
@@ -69,10 +117,114 @@ describe('ComboEditor', () => {
dirty: false,
type: 'required'
};
- fixture.detectChanges();
});
it('should create', () => {
- expect(component).toBeTruthy();
+ if (item) {
+ component.item = item;
+ fixture.detectChanges();
+
+ expect(component).toBeTruthy();
+ }
+ });
+
+ it('verify combo value', () => {
+ if (item) {
+ component.item = item;
+ fixture.detectChanges();
+
+ const formValue = component.comboEditorForm.get('value')?.value;
+
expect(component.itemLookup.get(formValue)?.value).toEqual(item.value);
+
expect(component.comboEditorForm.get('parameterReference')).toBeNull();
+
+ spyOn(component.ok, 'next');
+ component.okClicked();
+ expect(component.ok.next).toHaveBeenCalledWith(item.value);
+ }
+ });
+
+ it('verify combo not required with null value and default', () => {
+ if (item) {
+ item.value = null;
+ item.descriptor.required = false;
+
+ component.item = item;
+ fixture.detectChanges();
+
+ const formValue = component.comboEditorForm.get('value')?.value;
+
expect(component.itemLookup.get(formValue)?.value).toEqual(item.descriptor.defaultValue);
+
expect(component.comboEditorForm.get('parameterReference')).toBeNull();
+
+ spyOn(component.ok, 'next');
+ component.okClicked();
+
expect(component.ok.next).toHaveBeenCalledWith(item.descriptor.defaultValue);
+ }
+ });
+
+ it('verify combo not required with null value and no default', () => {
+ if (item) {
+ item.value = null;
+ item.descriptor.required = false;
+ item.descriptor.defaultValue = undefined;
+
+ component.item = item;
+ fixture.detectChanges();
+
+ const formValue = component.comboEditorForm.get('value')?.value;
+
expect(component.itemLookup.get(formValue)?.value).toEqual(item.value);
+
expect(component.comboEditorForm.get('parameterReference')).toBeNull();
+
+ spyOn(component.ok, 'next');
+ component.okClicked();
+ expect(component.ok.next).toHaveBeenCalledWith(item.value);
+ }
+ });
+
+ it('verify combo with parameter reference', () => {
+ if (item) {
+ item.value = '#{one}';
+
+ component.item = item;
+ component.getParameters = (sensitive: boolean) => {
+ return of(parameters);
+ };
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const formValue =
component.comboEditorForm.get('value')?.value;
+
expect(component.itemLookup.get(formValue)?.value).toEqual(item?.value);
+
expect(component.comboEditorForm.get('parameterReference')).toBeDefined();
+
+ const parameterReferenceValue =
component.comboEditorForm.get('parameterReference')?.value;
+
expect(component.itemLookup.get(parameterReferenceValue)?.value).toEqual(item?.value);
+
+ spyOn(component.ok, 'next');
+ component.okClicked();
+ expect(component.ok.next).toHaveBeenCalledWith(item?.value);
+ });
+ }
+ });
+
+ it('verify combo with missing parameter reference', () => {
+ if (item) {
+ item.value = '#{three}';
+
+ component.item = item;
+ component.getParameters = (sensitive: boolean) => {
+ return of(parameters);
+ };
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const formValue =
component.comboEditorForm.get('value')?.value;
+
expect(component.itemLookup.get(formValue)?.value).toEqual('#{' +
parameters[0].value + '}');
+
expect(component.comboEditorForm.get('parameterReference')).toBeDefined();
+
+ const parameterReferenceValue =
component.comboEditorForm.get('parameterReference')?.value;
+
expect(component.itemLookup.get(parameterReferenceValue)?.value).toEqual(item?.value);
+
+ spyOn(component.ok, 'next');
+ component.okClicked();
+ expect(component.ok.next).toHaveBeenCalledWith('#{' +
parameters[0].value + '}');
+ });
+ }
});
});
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.ts
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.ts
index 8750382e8e..574c2e6d34 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.ts
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/combo-editor/combo-editor.component.ts
@@ -25,12 +25,14 @@ import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { NgForOf, NgIf, NgTemplateOutlet } from '@angular/common';
import { NifiTooltipDirective } from
'../../../tooltips/nifi-tooltip.directive';
-import { PropertyDescriptor, AllowableValue, TextTipInput } from
'../../../../../state/shared';
+import { AllowableValue, Parameter, PropertyDescriptor, TextTipInput } from
'../../../../../state/shared';
import { MatOptionModule } from '@angular/material/core';
import { MatSelectModule } from '@angular/material/select';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TextTip } from '../../../tooltips/text-tip/text-tip.component';
import { A11yModule } from '@angular/cdk/a11y';
+import { Observable, take } from 'rxjs';
+import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
export interface AllowableValueItem extends AllowableValue {
id: number;
@@ -55,57 +57,32 @@ export interface AllowableValueItem extends AllowableValue {
NgForOf,
MatTooltipModule,
NgIf,
- A11yModule
+ A11yModule,
+ NgxSkeletonLoaderModule
],
styleUrls: ['./combo-editor.component.scss']
})
export class ComboEditor {
@Input() set item(item: PropertyItem) {
- this.itemLookup.clear();
- this.descriptor = item.descriptor;
- this.allowableValues = [];
-
- let i: number = 0;
- let selectedItem: AllowableValueItem | null = null;
-
- if (!this.descriptor.required) {
- const noValue: AllowableValueItem = {
- id: i++,
- displayName: 'No value',
- value: null
- };
- this.itemLookup.set(noValue.id, noValue);
- this.allowableValues.push(noValue);
-
- if (noValue.value == item.value) {
- selectedItem = noValue;
- }
+ if (item.value != null) {
+ this.configuredValue = item.value;
+ } else if (item.descriptor.defaultValue != null) {
+ this.configuredValue = item.descriptor.defaultValue;
}
- const allowableValueItems: AllowableValueItem[] =
this.descriptor.allowableValues.map(
- (allowableValueEntity) => {
- const allowableValue: AllowableValueItem = {
- ...allowableValueEntity.allowableValue,
- id: i++
- };
- this.itemLookup.set(allowableValue.id, allowableValue);
+ this.descriptor = item.descriptor;
+ this.sensitive = item.descriptor.sensitive;
- if (allowableValue.value == item.value) {
- selectedItem = allowableValue;
- }
+ this.itemSet = true;
+ this.initialAllowableValues();
+ }
- return allowableValue;
- }
- );
- this.allowableValues.push(...allowableValueItems);
+ @Input() set getParameters(getParameters: (sensitive: boolean) =>
Observable<Parameter[]>) {
+ this._getParameters = getParameters;
- if (selectedItem) {
- // mat-select does not have good support for options with null
value so we've
- // introduced a mapping to work around the shortcoming
- this.comboEditorForm.get('value')?.setValue(selectedItem.id);
- }
+ this.supportsParameters = getParameters != null;
+ this.initialAllowableValues();
}
- @Input() supportsParameters: boolean = false;
@Output() ok: EventEmitter<any> = new EventEmitter<any>();
@Output() cancel: EventEmitter<void> = new EventEmitter<void>();
@@ -113,21 +90,172 @@ export class ComboEditor {
protected readonly TextTip = TextTip;
itemLookup: Map<number, AllowableValueItem> = new Map<number,
AllowableValueItem>();
+ referencesParametersId: number = -1;
+ configuredParameterId: number = -1;
comboEditorForm: FormGroup;
descriptor!: PropertyDescriptor;
allowableValues!: AllowableValueItem[];
+ showParameterAllowableValues: boolean = false;
+ parameterAllowableValues!: AllowableValueItem[];
+
+ sensitive: boolean = false;
+ supportsParameters: boolean = false;
+ parametersLoaded: boolean = false;
+
+ itemSet: boolean = false;
+ configuredValue: string | null = null;
+ _getParameters!: (sensitive: boolean) => Observable<Parameter[]>;
+
constructor(private formBuilder: FormBuilder) {
this.comboEditorForm = this.formBuilder.group({
value: new FormControl(null, Validators.required)
});
}
+ initialAllowableValues(): void {
+ if (this.itemSet) {
+ this.itemLookup.clear();
+ this.allowableValues = [];
+ this.referencesParametersId = -1;
+
+ let i: number = 0;
+ let selectedItem: AllowableValueItem | null = null;
+
+ if (!this.descriptor.required) {
+ const noValue: AllowableValueItem = {
+ id: i++,
+ displayName: 'No value',
+ value: null
+ };
+ this.itemLookup.set(noValue.id, noValue);
+ this.allowableValues.push(noValue);
+
+ if (noValue.value == this.configuredValue) {
+ selectedItem = noValue;
+ }
+ }
+
+ if (this.descriptor.allowableValues) {
+ const allowableValueItems: AllowableValueItem[] =
this.descriptor.allowableValues.map(
+ (allowableValueEntity) => {
+ const allowableValue: AllowableValueItem = {
+ ...allowableValueEntity.allowableValue,
+ id: i++
+ };
+ this.itemLookup.set(allowableValue.id, allowableValue);
+
+ if (allowableValue.value == this.configuredValue) {
+ selectedItem = allowableValue;
+ }
+
+ return allowableValue;
+ }
+ );
+ this.allowableValues.push(...allowableValueItems);
+ }
+
+ if (this.supportsParameters) {
+ this.parametersLoaded = false;
+
+ // parameters are supported so add the item to support showing
+ // and hiding the parameter options select
+ const referencesParameterOption: AllowableValueItem = {
+ id: i++,
+ displayName: 'Reference Parameter...',
+ value: null
+ };
+ this.allowableValues.push(referencesParameterOption);
+ this.itemLookup.set(referencesParameterOption.id,
referencesParameterOption);
+
+ // record the item of the item to more easily identify this
item
+ this.referencesParametersId = referencesParameterOption.id;
+
+ // if the current value references a parameter auto select the
+ // references parameter item
+ if (this.referencesParameter(this.configuredValue)) {
+ selectedItem = referencesParameterOption;
+
+ // trigger allowable value changed to show the parameters
+ this.allowableValueChanged(this.referencesParametersId);
+ }
+
+ this._getParameters(this.sensitive)
+ .pipe(take(1))
+ .subscribe((parameters) => {
+ if (parameters.length > 0) {
+ // capture the value of i which will be the id of
the first
+ // parameter
+ this.configuredParameterId = i;
+
+ // create allowable values for each parameter
+ parameters.forEach((parameter) => {
+ const parameterItem: AllowableValueItem = {
+ id: i++,
+ displayName: parameter.name,
+ value: '#{' + parameter.name + '}',
+ description: parameter.description
+ };
+
this.parameterAllowableValues.push(parameterItem);
+ this.itemLookup.set(parameterItem.id,
parameterItem);
+
+ // if the configured parameter is still
available,
+ // capture the id, so we can auto select it
+ if (parameterItem.value ===
this.configuredValue) {
+ this.configuredParameterId =
parameterItem.id;
+ }
+ });
+
+ // if combo still set to reference a parameter,
set the default value
+ if (this.comboEditorForm.get('value')?.value ==
this.referencesParametersId) {
+
this.comboEditorForm.get('parameterReference')?.setValue(this.configuredParameterId);
+ }
+ }
+
+ this.parametersLoaded = true;
+ });
+ } else {
+ this.parameterAllowableValues = [];
+ }
+
+ if (selectedItem) {
+ // mat-select does not have good support for options with null
value so we've
+ // introduced a mapping to work around the shortcoming
+ this.comboEditorForm.get('value')?.setValue(selectedItem.id);
+ }
+ }
+ }
+
+ referencesParameter(value: string | null): boolean {
+ if (value) {
+ return value.startsWith('#{') && value.endsWith('}');
+ }
+
+ return false;
+ }
+
preventDrag(event: MouseEvent): void {
event.stopPropagation();
}
+ allowableValueChanged(value: number): void {
+ this.showParameterAllowableValues = value ===
this.referencesParametersId;
+
+ if (this.showParameterAllowableValues) {
+ if (this.configuredParameterId === -1) {
+ this.comboEditorForm.addControl('parameterReference', new
FormControl(null, Validators.required));
+ } else {
+ this.comboEditorForm.addControl(
+ 'parameterReference',
+ new FormControl(this.configuredParameterId,
Validators.required)
+ );
+ }
+ } else {
+ this.comboEditorForm.removeControl('parameterReference');
+ }
+ }
+
getComboPlaceholder(): string {
const valueControl: AbstractControl | null =
this.comboEditorForm.get('value');
if (valueControl) {
@@ -150,7 +278,21 @@ export class ComboEditor {
if (valueControl) {
const selectedItem: AllowableValueItem | undefined =
this.itemLookup.get(valueControl.value);
if (selectedItem) {
- this.ok.next(selectedItem.value);
+ // if the value currently references a parameter emit the
parameter, get the parameter reference control and emit that value
+ if (selectedItem.id == this.referencesParametersId) {
+ const parameterReferenceControl: AbstractControl | null =
+ this.comboEditorForm.get('parameterReference');
+ if (parameterReferenceControl) {
+ const selectedParameterItem: AllowableValueItem |
undefined = this.itemLookup.get(
+ parameterReferenceControl.value
+ );
+ if (selectedParameterItem) {
+ this.ok.next(selectedParameterItem.value);
+ }
+ }
+ } else {
+ this.ok.next(selectedItem.value);
+ }
}
}
}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.spec.ts
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.spec.ts
index d28d0a96a7..083a591e6b 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.spec.ts
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.spec.ts
@@ -19,6 +19,7 @@ import { ComponentFixture, TestBed } from
'@angular/core/testing';
import { NfEditor } from './nf-editor.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { PropertyItem } from '../../property-table.component';
describe('NfEditor', () => {
let component: NfEditor;
@@ -30,10 +31,84 @@ describe('NfEditor', () => {
});
fixture = TestBed.createComponent(NfEditor);
component = fixture.componentInstance;
- fixture.detectChanges();
});
it('should create', () => {
+ fixture.detectChanges();
expect(component).toBeTruthy();
});
+
+ it('verify value set', () => {
+ const value: string = 'my-group-id';
+ const item: PropertyItem = {
+ property: 'group.id',
+ value,
+ descriptor: {
+ name: 'group.id',
+ displayName: 'Group ID',
+ description:
+ "A Group ID is used to identify consumers that are within
the same consumer group. Corresponds to Kafka's 'group.id' property.",
+ required: true,
+ sensitive: false,
+ dynamic: false,
+ supportsEl: true,
+ expressionLanguageScope: 'Environment variables defined at JVM
level and system properties',
+ dependencies: []
+ },
+ id: 3,
+ triggerEdit: false,
+ deleted: false,
+ added: false,
+ dirty: false,
+ type: 'required'
+ };
+
+ component.item = item;
+ fixture.detectChanges();
+
+ expect(component.nfEditorForm.get('value')?.value).toEqual(value);
+ expect(component.nfEditorForm.get('value')?.disabled).toBeFalse();
+
expect(component.nfEditorForm.get('setEmptyString')?.value).toBeFalse();
+
+ spyOn(component.ok, 'next');
+ component.okClicked();
+ expect(component.ok.next).toHaveBeenCalledWith(value);
+ });
+
+ it('verify empty value set', () => {
+ const value: string = '';
+ const item: PropertyItem = {
+ property: 'group.id',
+ value,
+ descriptor: {
+ name: 'group.id',
+ displayName: 'Group ID',
+ description:
+ "A Group ID is used to identify consumers that are within
the same consumer group. Corresponds to Kafka's 'group.id' property.",
+ required: true,
+ sensitive: false,
+ dynamic: false,
+ supportsEl: true,
+ expressionLanguageScope: 'Environment variables defined at JVM
level and system properties',
+ dependencies: []
+ },
+ id: 3,
+ triggerEdit: false,
+ deleted: false,
+ added: false,
+ dirty: false,
+ type: 'required'
+ };
+
+ component.item = item;
+ fixture.detectChanges();
+
+ expect(component.nfEditorForm.get('value')?.value).toEqual(value);
+ expect(component.nfEditorForm.get('value')?.disabled).toBeTruthy();
+
expect(component.nfEditorForm.get('setEmptyString')?.value).toBeTruthy();
+
+ spyOn(component.ok, 'next');
+ component.okClicked();
+ expect(component.ok.next).toHaveBeenCalledWith(value);
+ });
});
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts
index 6086b635f5..acf4feec45 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/editors/nf-editor/nf-editor.component.ts
@@ -15,18 +15,7 @@
* limitations under the License.
*/
-import {
- AfterViewInit,
- Component,
- ElementRef,
- EventEmitter,
- Input,
- OnDestroy,
- Output,
- Renderer2,
- ViewChild,
- ViewContainerRef
-} from '@angular/core';
+import { Component, EventEmitter, Input, OnDestroy, Output, Renderer2,
ViewContainerRef } from '@angular/core';
import { PropertyItem } from '../../property-table.component';
import { CdkDrag, CdkDragHandle } from '@angular/cdk/drag-drop';
import { AbstractControl, FormBuilder, FormControl, FormGroup,
ReactiveFormsModule, Validators } from '@angular/forms';
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.html
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.html
index c069eb5ad9..563f9cc304 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.html
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.html
@@ -145,6 +145,7 @@
<combo-editor
*ngIf="hasAllowableValues(editorItem); else nfEditor"
[item]="editorItem"
+ [getParameters]="getParameters"
(ok)="savePropertyValue(editorItem, $event)"
(cancel)="closeEditor()"></combo-editor>
<ng-template #nfEditor>
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.ts
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.ts
index 36af490253..0900ead04d 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.ts
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/property-table/property-table.component.ts
@@ -347,8 +347,8 @@ export class PropertyTable implements AfterViewInit,
ControlValueAccessor {
}
resolvePropertyValue(property: Property): string | null {
- const allowableValues: AllowableValueEntity[] =
property.descriptor.allowableValues;
- if (this.nifiCommon.isEmpty(allowableValues)) {
+ const allowableValues: AllowableValueEntity[] | undefined =
property.descriptor.allowableValues;
+ if (allowableValues == null ||
this.nifiCommon.isEmpty(allowableValues)) {
return property.value;
} else {
const allowableValue: AllowableValueEntity | undefined =
allowableValues.find(
@@ -392,7 +392,7 @@ export class PropertyTable implements AfterViewInit,
ControlValueAccessor {
// TODO - add Input() for supportsGoTo? currently only false in
summary table
const descriptor: PropertyDescriptor = item.descriptor;
- if (item.value && descriptor.identifiesControllerService) {
+ if (item.value && descriptor.identifiesControllerService &&
descriptor.allowableValues) {
return descriptor.allowableValues.some(
(entity: AllowableValueEntity) => entity.allowableValue.value
== item.value
);