diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ParameterProvidersEntity.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ParameterProvidersEntity.java index 900bdfb4c3..88972d4d2f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ParameterProvidersEntity.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/entity/ParameterProvidersEntity.java @@ -16,7 +16,12 @@ */ package org.apache.nifi.web.api.entity; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.xml.bind.annotation.XmlRootElement; +import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import org.apache.nifi.web.api.dto.util.TimeAdapter; + +import java.util.Date; import java.util.Set; /** @@ -27,6 +32,8 @@ public class ParameterProvidersEntity extends Entity { private Set parameterProviders; + private Date currentTime; + /** * @return list of parameter providers that are being serialized */ @@ -38,4 +45,18 @@ public class ParameterProvidersEntity extends Entity { this.parameterProviders = parameterProviders; } + /** + * @return current time on the server + */ + @XmlJavaTypeAdapter(TimeAdapter.class) + @Schema(description = "The current time on the system.", + type = "string" + ) + public Date getCurrentTime() { + return currentTime; + } + + public void setCurrentTime(Date currentTime) { + this.currentTime = currentTime; + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/ParameterProvidersEndpointMerger.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/ParameterProvidersEndpointMerger.java index 079922f5d2..1b3e6807c6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/ParameterProvidersEndpointMerger.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/coordination/http/endpoints/ParameterProvidersEndpointMerger.java @@ -23,6 +23,7 @@ import org.apache.nifi.web.api.entity.ParameterProviderEntity; import org.apache.nifi.web.api.entity.ParameterProvidersEntity; import java.net.URI; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -60,6 +61,7 @@ public class ParameterProvidersEndpointMerger extends AbstractSingleEntityEndpoi } clientEntity.setParameterProviders(new HashSet<>(providerEntities.values())); + clientEntity.setCurrentTime(new Date()); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java index fd9bb8c5d4..6ed92d1d73 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/FlowResource.java @@ -672,6 +672,7 @@ public class FlowResource extends ApplicationResource { // create the response entity final ParameterProvidersEntity entity = new ParameterProvidersEntity(); entity.setParameterProviders(parameterProviders); + entity.setCurrentTime(new Date()); // generate the response return generateOkResponse(entity).build(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/service/access-policy.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/service/access-policy.service.ts index 34ab4bf959..8fb0800b80 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/service/access-policy.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/service/access-policy.service.ts @@ -33,19 +33,6 @@ export class AccessPolicyService { private nifiCommon: NiFiCommon ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - createAccessPolicy(resourceAction: ResourceAction): Observable { let resource: string = `/${resourceAction.resource}`; if (resourceAction.resourceIdentifier) { @@ -92,12 +79,12 @@ export class AccessPolicyService { } }; - return this.httpClient.put(this.stripProtocol(accessPolicy.uri), payload); + return this.httpClient.put(this.nifiCommon.stripProtocol(accessPolicy.uri), payload); } deleteAccessPolicy(accessPolicy: AccessPolicyEntity): Observable { const revision: any = this.client.getRevision(accessPolicy); - return this.httpClient.delete(this.stripProtocol(accessPolicy.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(accessPolicy.uri), { params: revision }); } getUsers(): Observable { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.ts index b22bdeb566..5d52f50df3 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/access-policies/ui/component-access-policies/component-access-policies.component.ts @@ -306,6 +306,7 @@ export class ComponentAccessPolicies implements OnInit, OnDestroy { return 'icon-label'; case 'remote-process-groups': return 'icon-group-remote'; + case 'parameter-providers': case 'parameter-contexts': return 'icon-drop'; } @@ -329,6 +330,8 @@ export class ComponentAccessPolicies implements OnInit, OnDestroy { return 'Remote Process Group'; case 'parameter-contexts': return 'Parameter Contexts'; + case 'parameter-providers': + return 'Parameter Provider'; } return 'Process Group'; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.ts index 2ae859dfce..666aa21833 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/counters/ui/counter-listing/counter-table/counter-table.component.ts @@ -42,11 +42,13 @@ export class CounterTable implements AfterViewInit { @Input() initialSortColumn: 'context' | 'name' | 'value' = 'context'; @Input() initialSortDirection: 'asc' | 'desc' = 'asc'; + activeSort: Sort = { + active: this.initialSortColumn, + direction: this.initialSortDirection + }; + @Input() set counters(counterEntities: CounterEntity[]) { - this.dataSource.data = this.sortEntities(counterEntities, { - active: this.initialSortColumn, - direction: this.initialSortDirection - }); + this.dataSource.data = this.sortEntities(counterEntities, this.activeSort); this.dataSource.filterPredicate = (data: CounterEntity, filter: string) => { const { filterTerm, filterColumn } = JSON.parse(filter); @@ -128,6 +130,7 @@ export class CounterTable implements AfterViewInit { } sortData(sort: Sort) { + this.activeSort = sort; this.dataSource.data = this.sortEntities(this.dataSource.data, sort); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/controller-service.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/controller-service.service.ts index c354935607..cb3ffe42a6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/controller-service.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/controller-service.service.ts @@ -16,34 +16,22 @@ */ import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { EMPTY, Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { Client } from '../../../service/client.service'; import { NiFiCommon } from '../../../service/nifi-common.service'; -import { ControllerServiceEntity } from '../../../state/shared'; import { - ConfigureControllerServiceRequest, + ControllerServiceCreator, + ControllerServiceEntity, CreateControllerServiceRequest, - DeleteControllerServiceRequest -} from '../state/controller-services'; + PropertyDescriptorRetriever +} from '../../../state/shared'; +import { ConfigureControllerServiceRequest, DeleteControllerServiceRequest } from '../state/controller-services'; @Injectable({ providedIn: 'root' }) -export class ControllerServiceService { +export class ControllerServiceService implements ControllerServiceCreator, PropertyDescriptorRetriever { private static readonly API: string = '../nifi-api'; - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - constructor( private httpClient: HttpClient, private client: Client, @@ -69,17 +57,20 @@ export class ControllerServiceService { } createControllerService(createControllerService: CreateControllerServiceRequest): Observable { - const processGroupId: string = createControllerService.processGroupId; - return this.httpClient.post( - `${ControllerServiceService.API}/process-groups/${processGroupId}/controller-services`, - { - revision: createControllerService.revision, - component: { - bundle: createControllerService.controllerServiceBundle, - type: createControllerService.controllerServiceType + if (createControllerService.processGroupId) { + const processGroupId: string = createControllerService.processGroupId; + return this.httpClient.post( + `${ControllerServiceService.API}/process-groups/${processGroupId}/controller-services`, + { + revision: createControllerService.revision, + component: { + bundle: createControllerService.controllerServiceBundle, + type: createControllerService.controllerServiceType + } } - } - ); + ); + } + return EMPTY; } getPropertyDescriptor(id: string, propertyName: string, sensitive: boolean): Observable { @@ -94,7 +85,7 @@ export class ControllerServiceService { updateControllerService(configureControllerService: ConfigureControllerServiceRequest): Observable { return this.httpClient.put( - this.stripProtocol(configureControllerService.uri), + this.nifiCommon.stripProtocol(configureControllerService.uri), configureControllerService.payload ); } @@ -102,6 +93,6 @@ export class ControllerServiceService { deleteControllerService(deleteControllerService: DeleteControllerServiceRequest): Observable { const entity: ControllerServiceEntity = deleteControllerService.controllerService; const revision: any = this.client.getRevision(entity); - return this.httpClient.delete(this.stripProtocol(entity.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision }); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/flow.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/flow.service.ts index b2acac8580..8d841d986c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/flow.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/flow.service.ts @@ -38,12 +38,12 @@ import { UpdateComponentRequest, UploadProcessGroupRequest } from '../state/flow'; -import { ComponentType } from '../../../state/shared'; +import { ComponentType, PropertyDescriptorRetriever } from '../../../state/shared'; import { Client } from '../../../service/client.service'; import { NiFiCommon } from '../../../service/nifi-common.service'; @Injectable({ providedIn: 'root' }) -export class FlowService { +export class FlowService implements PropertyDescriptorRetriever { private static readonly API: string = '../nifi-api'; constructor( @@ -53,19 +53,6 @@ export class FlowService { private nifiCommon: NiFiCommon ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - getFlow(processGroupId = 'root'): Observable { // TODO - support uiOnly... this would mean that we need to load the entire resource prior to editing return this.httpClient.get(`${FlowService.API}/flow/process-groups/${processGroupId}`); @@ -212,13 +199,13 @@ export class FlowService { updateComponent(updateComponent: UpdateComponentRequest): Observable { // return throwError('API Error'); - return this.httpClient.put(this.stripProtocol(updateComponent.uri), updateComponent.payload); + return this.httpClient.put(this.nifiCommon.stripProtocol(updateComponent.uri), updateComponent.payload); } deleteComponent(deleteComponent: DeleteComponentRequest): Observable { // return throwError('API Error'); const revision: any = this.client.getRevision(deleteComponent.entity); - return this.httpClient.delete(this.stripProtocol(deleteComponent.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(deleteComponent.uri), { params: revision }); } createSnippet(snippet: Snippet): Observable { @@ -250,7 +237,7 @@ export class FlowService { disconnectedNodeAcknowledged: false, state: 'RUN_ONCE' }; - return this.httpClient.put(`${this.stripProtocol(request.uri)}/run-status`, startRequest); + return this.httpClient.put(`${this.nifiCommon.stripProtocol(request.uri)}/run-status`, startRequest); } startComponent(request: StartComponentRequest): Observable { @@ -259,7 +246,7 @@ export class FlowService { disconnectedNodeAcknowledged: false, state: request.type === ComponentType.RemoteProcessGroup ? 'TRANSMITTING' : 'RUNNING' }; - return this.httpClient.put(`${this.stripProtocol(request.uri)}/run-status`, startRequest); + return this.httpClient.put(`${this.nifiCommon.stripProtocol(request.uri)}/run-status`, startRequest); } stopComponent(request: StopComponentRequest): Observable { @@ -268,7 +255,7 @@ export class FlowService { disconnectedNodeAcknowledged: false, state: 'STOPPED' }; - return this.httpClient.put(`${this.stripProtocol(request.uri)}/run-status`, stopRequest); + return this.httpClient.put(`${this.nifiCommon.stripProtocol(request.uri)}/run-status`, stopRequest); } startProcessGroup(request: StartProcessGroupRequest): Observable { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/parameter.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/parameter.service.ts index d8e1731c68..009a01c4ca 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/parameter.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/parameter.service.ts @@ -30,19 +30,6 @@ export class ParameterService { private nifiCommon: NiFiCommon ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - getParameterContext(id: string, includeInheritedParameters: boolean): Observable { return this.httpClient.get(`${ParameterService.API}/parameter-contexts/${id}`, { params: { @@ -59,10 +46,10 @@ export class ParameterService { } pollParameterContextUpdate(updateRequest: ParameterContextUpdateRequest): Observable { - return this.httpClient.get(this.stripProtocol(updateRequest.uri)); + return this.httpClient.get(this.nifiCommon.stripProtocol(updateRequest.uri)); } deleteParameterContextUpdate(updateRequest: ParameterContextUpdateRequest): Observable { - return this.httpClient.delete(this.stripProtocol(updateRequest.uri)); + return this.httpClient.delete(this.nifiCommon.stripProtocol(updateRequest.uri)); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/queue.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/queue.service.ts index df242809f3..f6c657bc53 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/queue.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/service/queue.service.ts @@ -30,19 +30,6 @@ export class QueueService { private nifiCommon: NiFiCommon ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - submitEmptyQueueRequest(emptyQueueRequest: SubmitEmptyQueueRequest): Observable { return this.httpClient.post( `${QueueService.API}/flowfile-queues/${emptyQueueRequest.connectionId}/drop-requests`, @@ -58,10 +45,10 @@ export class QueueService { } pollEmptyQueueRequest(dropRequest: DropRequest): Observable { - return this.httpClient.get(this.stripProtocol(dropRequest.uri)); + return this.httpClient.get(this.nifiCommon.stripProtocol(dropRequest.uri)); } deleteEmptyQueueRequest(dropRequest: DropRequest): Observable { - return this.httpClient.delete(this.stripProtocol(dropRequest.uri)); + return this.httpClient.delete(this.nifiCommon.stripProtocol(dropRequest.uri)); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.actions.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.actions.ts index 401f0da757..7dd7c9ff59 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.actions.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.actions.ts @@ -19,7 +19,6 @@ import { createAction, props } from '@ngrx/store'; import { ConfigureControllerServiceRequest, ConfigureControllerServiceSuccess, - CreateControllerServiceRequest, CreateControllerServiceSuccess, DeleteControllerServiceRequest, DeleteControllerServiceSuccess, @@ -28,6 +27,7 @@ import { SelectControllerServiceRequest } from './index'; import { + CreateControllerServiceRequest, DisableControllerServiceDialogRequest, EditControllerServiceDialogRequest, SetEnableControllerServiceDialogRequest diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts index 4efe9641be..7997d62ca7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/controller-services.effects.ts @@ -18,20 +18,7 @@ import { Injectable } from '@angular/core'; import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import * as ControllerServicesActions from './controller-services.actions'; -import { - catchError, - combineLatest, - filter, - from, - map, - NEVER, - Observable, - of, - switchMap, - take, - takeUntil, - tap -} from 'rxjs'; +import { catchError, combineLatest, filter, from, map, NEVER, of, switchMap, take, takeUntil, tap } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { NiFiState } from '../../../../state'; @@ -45,17 +32,10 @@ import { ControllerServiceReferencingComponent, EditParameterRequest, EditParameterResponse, - InlineServiceCreationRequest, - InlineServiceCreationResponse, - NewPropertyDialogRequest, - NewPropertyDialogResponse, Parameter, ParameterEntity, - Property, - PropertyDescriptor, UpdateControllerServiceRequest } from '../../../../state/shared'; -import { NewPropertyDialog } from '../../../../ui/common/new-property-dialog/new-property-dialog.component'; import { Router } from '@angular/router'; import { ExtensionTypesService } from '../../../../service/extension-types.service'; import { selectCurrentProcessGroupId, selectSaving } from './controller-services.selectors'; @@ -68,6 +48,7 @@ import * as ParameterActions from '../parameter/parameter.actions'; import { ParameterService } from '../../service/parameter.service'; import { EnableControllerService } from '../../../../ui/common/controller-service/enable-controller-service/enable-controller-service.component'; import { DisableControllerService } from '../../../../ui/common/controller-service/disable-controller-service/disable-controller-service.component'; +import { PropertyTableHelperService } from '../../../../service/property-table-helper.service'; @Injectable() export class ControllerServicesEffects { @@ -80,7 +61,8 @@ export class ControllerServicesEffects { private parameterService: ParameterService, private extensionTypesService: ExtensionTypesService, private dialog: MatDialog, - private router: Router + private router: Router, + private propertyTableHelperService: PropertyTableHelperService ) {} loadControllerServices$ = createEffect(() => @@ -225,36 +207,8 @@ export class ControllerServicesEffects { editDialogReference.componentInstance.saving$ = this.store.select(selectSaving); - editDialogReference.componentInstance.createNewProperty = ( - existingProperties: string[], - allowsSensitive: boolean - ): Observable => { - const dialogRequest: NewPropertyDialogRequest = { existingProperties, allowsSensitive }; - const newPropertyDialogReference = this.dialog.open(NewPropertyDialog, { - data: dialogRequest, - panelClass: 'small-dialog' - }); - - return newPropertyDialogReference.componentInstance.newProperty.pipe( - take(1), - switchMap((dialogResponse: NewPropertyDialogResponse) => { - return this.controllerServiceService - .getPropertyDescriptor(request.id, dialogResponse.name, dialogResponse.sensitive) - .pipe( - take(1), - map((response) => { - newPropertyDialogReference.close(); - - return { - property: dialogResponse.name, - value: null, - descriptor: response.propertyDescriptor - }; - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewProperty = + this.propertyTableHelperService.createNewProperty(request.id, this.controllerServiceService); const goTo = (commands: string[], destination: string): void => { if (editDialogReference.componentInstance.editControllerServiceForm.dirty) { @@ -393,85 +347,21 @@ export class ControllerServicesEffects { }); }; - editDialogReference.componentInstance.createNewService = ( - serviceRequest: InlineServiceCreationRequest - ): Observable => { - const descriptor: PropertyDescriptor = serviceRequest.descriptor; - - // fetch all services that implement the requested service api - return this.extensionTypesService - .getImplementingControllerServiceTypes( - // @ts-ignore - descriptor.identifiesControllerService, - descriptor.identifiesControllerServiceBundle - ) - .pipe( - take(1), - switchMap((implementingTypesResponse) => { - // show the create controller service dialog with the types that implemented the interface - const createServiceDialogReference = this.dialog.open(CreateControllerService, { - data: { - controllerServiceTypes: implementingTypesResponse.controllerServiceTypes - }, - panelClass: 'medium-dialog' - }); - - return createServiceDialogReference.componentInstance.createControllerService.pipe( - take(1), - switchMap((controllerServiceType) => { - // typically this sequence would be implemented with ngrx actions, however we are - // currently in an edit session and we need to return both the value (new service id) - // and updated property descriptor so the table renders correctly - return this.controllerServiceService - .createControllerService({ - revision: { - clientId: this.client.getClientId(), - version: 0 - }, - processGroupId, - controllerServiceType: controllerServiceType.type, - controllerServiceBundle: controllerServiceType.bundle - }) - .pipe( - take(1), - switchMap((createResponse) => { - // dispatch an inline create service success action so the new service is in the state - this.store.dispatch( - ControllerServicesActions.inlineCreateControllerServiceSuccess( - { - response: { - controllerService: createResponse - } - } - ) - ); - - // fetch an updated property descriptor - return this.controllerServiceService - .getPropertyDescriptor(serviceId, descriptor.name, false) - .pipe( - take(1), - map((descriptorResponse) => { - createServiceDialogReference.close(); - - return { - value: createResponse.id, - descriptor: - descriptorResponse.propertyDescriptor - }; - }) - ); - }), - catchError((error) => { - // TODO - show error - return NEVER; - }) - ); - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewService = + this.propertyTableHelperService.createNewService( + request.id, + this.controllerServiceService, + this.controllerServiceService, + processGroupId, + (createResponse) => + this.store.dispatch( + ControllerServicesActions.inlineCreateControllerServiceSuccess({ + response: { + controllerService: createResponse + } + }) + ) + ); editDialogReference.componentInstance.editControllerService .pipe(takeUntil(editDialogReference.afterClosed())) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/index.ts index b9551f7784..4cafd6f50e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/controller-services/index.ts @@ -31,13 +31,6 @@ export interface LoadControllerServicesResponse { loadedTimestamp: string; } -export interface CreateControllerServiceRequest { - processGroupId: string; - controllerServiceType: string; - controllerServiceBundle: Bundle; - revision: Revision; -} - export interface CreateControllerServiceSuccess { controllerService: ControllerServiceEntity; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts index bb2208c6db..1af9e879cf 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.effects.ts @@ -69,14 +69,8 @@ import { ComponentType, EditParameterRequest, EditParameterResponse, - InlineServiceCreationRequest, - InlineServiceCreationResponse, - NewPropertyDialogRequest, - NewPropertyDialogResponse, Parameter, - ParameterEntity, - Property, - PropertyDescriptor + ParameterEntity } from '../../../../state/shared'; import { Router } from '@angular/router'; import { Client } from '../../../../service/client.service'; @@ -86,7 +80,6 @@ import { selectProcessorTypes } from '../../../../state/extension-types/extensio import { NiFiState } from '../../../../state'; import { CreateProcessor } from '../../ui/canvas/items/processor/create-processor/create-processor.component'; import { EditProcessor } from '../../ui/canvas/items/processor/edit-processor/edit-processor.component'; -import { NewPropertyDialog } from '../../../../ui/common/new-property-dialog/new-property-dialog.component'; import { BirdseyeView } from '../../service/birdseye-view.service'; import { CreateProcessGroup } from '../../ui/canvas/items/process-group/create-process-group/create-process-group.component'; import { CreateConnection } from '../../ui/canvas/items/connection/create-connection/create-connection.component'; @@ -94,13 +87,13 @@ import { EditConnectionComponent } from '../../ui/canvas/items/connection/edit-c import { OkDialog } from '../../../../ui/common/ok-dialog/ok-dialog.component'; import { GroupComponents } from '../../ui/canvas/items/process-group/group-components/group-components.component'; import { EditProcessGroup } from '../../ui/canvas/items/process-group/edit-process-group/edit-process-group.component'; -import { CreateControllerService } from '../../../../ui/common/controller-service/create-controller-service/create-controller-service.component'; import { ExtensionTypesService } from '../../../../service/extension-types.service'; import { ControllerServiceService } from '../../service/controller-service.service'; import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component'; import { EditParameterDialog } from '../../../../ui/common/edit-parameter-dialog/edit-parameter-dialog.component'; import { selectParameterSaving } from '../parameter/parameter.selectors'; import { ParameterService } from '../../service/parameter.service'; +import { PropertyTableHelperService } from '../../../../service/property-table-helper.service'; @Injectable() export class FlowEffects { @@ -117,7 +110,8 @@ export class FlowEffects { private birdseyeView: BirdseyeView, private connectionManager: ConnectionManager, private router: Router, - private dialog: MatDialog + private dialog: MatDialog, + private propertyTableHelperService: PropertyTableHelperService ) {} reloadFlow$ = createEffect(() => @@ -852,36 +846,8 @@ export class FlowEffects { editDialogReference.componentInstance.saving$ = this.store.select(selectSaving); - editDialogReference.componentInstance.createNewProperty = ( - existingProperties: string[], - allowsSensitive: boolean - ): Observable => { - const dialogRequest: NewPropertyDialogRequest = { existingProperties, allowsSensitive }; - const newPropertyDialogReference = this.dialog.open(NewPropertyDialog, { - data: dialogRequest, - panelClass: 'small-dialog' - }); - - return newPropertyDialogReference.componentInstance.newProperty.pipe( - take(1), - switchMap((dialogResponse: NewPropertyDialogResponse) => { - return this.flowService - .getPropertyDescriptor(processorId, dialogResponse.name, dialogResponse.sensitive) - .pipe( - take(1), - map((response) => { - newPropertyDialogReference.close(); - - return { - property: dialogResponse.name, - value: null, - descriptor: response.propertyDescriptor - }; - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewProperty = + this.propertyTableHelperService.createNewProperty(processorId, this.flowService); const goTo = (commands: string[], destination: string): void => { if (editDialogReference.componentInstance.editProcessorForm.dirty) { @@ -1013,74 +979,13 @@ export class FlowEffects { }); }; - editDialogReference.componentInstance.createNewService = ( - serviceRequest: InlineServiceCreationRequest - ): Observable => { - const descriptor: PropertyDescriptor = serviceRequest.descriptor; - - // fetch all services that implement the requested service api - return this.extensionTypesService - .getImplementingControllerServiceTypes( - // @ts-ignore - descriptor.identifiesControllerService, - descriptor.identifiesControllerServiceBundle - ) - .pipe( - take(1), - switchMap((implementingTypesResponse) => { - // show the create controller service dialog with the types that implemented the interface - const createServiceDialogReference = this.dialog.open(CreateControllerService, { - data: { - controllerServiceTypes: implementingTypesResponse.controllerServiceTypes - }, - panelClass: 'medium-dialog' - }); - - return createServiceDialogReference.componentInstance.createControllerService.pipe( - take(1), - switchMap((controllerServiceType) => { - // typically this sequence would be implemented with ngrx actions, however we are - // currently in an edit session and we need to return both the value (new service id) - // and updated property descriptor so the table renders correctly - return this.controllerServiceService - .createControllerService({ - revision: { - clientId: this.client.getClientId(), - version: 0 - }, - processGroupId, - controllerServiceType: controllerServiceType.type, - controllerServiceBundle: controllerServiceType.bundle - }) - .pipe( - take(1), - switchMap((createReponse) => { - // fetch an updated property descriptor - return this.flowService - .getPropertyDescriptor(processorId, descriptor.name, false) - .pipe( - take(1), - map((descriptorResponse) => { - createServiceDialogReference.close(); - - return { - value: createReponse.id, - descriptor: - descriptorResponse.propertyDescriptor - }; - }) - ); - }), - catchError((error) => { - // TODO - show error - return NEVER; - }) - ); - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewService = + this.propertyTableHelperService.createNewService( + processorId, + this.controllerServiceService, + this.flowService, + processGroupId + ); editDialogReference.componentInstance.editProcessor .pipe(takeUntil(editDialogReference.afterClosed())) @@ -2331,7 +2236,7 @@ export class FlowEffects { return from(this.flowService.getProcessGroup(request.id)).pipe( map((response) => FlowActions.loadChildProcessGroupSuccess({ - response: response.component + response }) ), catchError((error) => of(FlowActions.flowApiError({ error: error.error }))) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.reducer.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.reducer.ts index 8a1af18d27..87e48cb1f5 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.reducer.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/state/flow/flow.reducer.ts @@ -326,10 +326,7 @@ export const flowReducer = createReducer( if (collection) { const componentIndex: number = collection.findIndex((f: any) => response.component.id === f.id); if (componentIndex > -1) { - collection[componentIndex] = { - ...collection[componentIndex], - ...response.component - }; + collection[componentIndex] = response.component; } } @@ -343,10 +340,7 @@ export const flowReducer = createReducer( if (collection) { const componentIndex: number = collection.findIndex((f: any) => response.component.id === f.id); if (componentIndex > -1) { - collection[componentIndex] = { - ...collection[componentIndex], - ...response.component - }; + collection[componentIndex] = response.component; } } @@ -361,10 +355,7 @@ export const flowReducer = createReducer( if (collection) { const componentIndex: number = collection.findIndex((f: any) => response.id === f.id); if (componentIndex > -1) { - collection[componentIndex] = { - ...collection[componentIndex], - ...response.component - }; + collection[componentIndex] = response; } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.spec.ts index 34cf6502be..fccb8a6661 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/flow-designer/ui/controller-service/controller-services.component.spec.ts @@ -22,6 +22,8 @@ import { provideMockStore } from '@ngrx/store/testing'; import { initialState } from '../../state/controller-services/controller-services.reducer'; import { RouterTestingModule } from '@angular/router/testing'; import { Component } from '@angular/core'; +import { ControllerServicesModule } from './controller-services.module'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; describe('ControllerServices', () => { let component: ControllerServices; @@ -37,7 +39,7 @@ describe('ControllerServices', () => { beforeEach(() => { TestBed.configureTestingModule({ declarations: [ControllerServices], - imports: [RouterTestingModule, MockNavigation], + imports: [RouterTestingModule, MockNavigation, ControllerServicesModule, HttpClientTestingModule], providers: [ provideMockStore({ initialState diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/service/parameter-contexts.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/service/parameter-contexts.service.ts index d013707af0..ae6b5cc2ae 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/service/parameter-contexts.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/service/parameter-contexts.service.ts @@ -37,19 +37,6 @@ export class ParameterContextService { private nifiCommon: NiFiCommon ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - getParameterContexts(): Observable { return this.httpClient.get(`${ParameterContextService.API}/flow/parameter-contexts`); } @@ -77,11 +64,11 @@ export class ParameterContextService { } pollParameterContextUpdate(updateRequest: ParameterContextUpdateRequest): Observable { - return this.httpClient.get(this.stripProtocol(updateRequest.uri)); + return this.httpClient.get(this.nifiCommon.stripProtocol(updateRequest.uri)); } deleteParameterContextUpdate(updateRequest: ParameterContextUpdateRequest): Observable { - return this.httpClient.delete(this.stripProtocol(updateRequest.uri)); + return this.httpClient.delete(this.nifiCommon.stripProtocol(updateRequest.uri)); } deleteParameterContext(deleteParameterContext: DeleteParameterContextRequest): Observable { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-listing.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-listing.module.ts index 69787cc17e..24fdbcf7be 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-listing.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-listing.module.ts @@ -28,7 +28,7 @@ import { RouterLink } from '@angular/router'; @NgModule({ declarations: [ParameterContextListing, ParameterContextTable], - exports: [ParameterContextListing], + exports: [ParameterContextListing, ParameterContextTable], imports: [ CommonModule, NgxSkeletonLoaderModule, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.ts index 6925f25f9b..9794280610 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/parameter-contexts/ui/parameter-context-listing/parameter-context-table/parameter-context-table.component.ts @@ -31,12 +31,13 @@ import { CurrentUser } from '../../../../../state/current-user'; export class ParameterContextTable { @Input() initialSortColumn: 'name' | 'provider' | 'description' = 'name'; @Input() initialSortDirection: 'asc' | 'desc' = 'asc'; + activeSort: Sort = { + active: this.initialSortColumn, + direction: this.initialSortDirection + }; @Input() set parameterContexts(parameterContextEntities: ParameterContextEntity[]) { - this.dataSource.data = this.sortEntities(parameterContextEntities, { - active: this.initialSortColumn, - direction: this.initialSortDirection - }); + this.dataSource.data = this.sortEntities(parameterContextEntities, this.activeSort); } @Input() selectedParameterContextId!: string; @@ -109,6 +110,7 @@ export class ParameterContextTable { } sortData(sort: Sort) { + this.activeSort = sort; this.dataSource.data = this.sortEntities(this.dataSource.data, sort); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/provenance/service/provenance.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/provenance/service/provenance.service.ts index d3d73883e7..3e1d238699 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/provenance/service/provenance.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/provenance/service/provenance.service.ts @@ -16,7 +16,7 @@ */ import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { NiFiCommon } from '../../../service/nifi-common.service'; import { ProvenanceRequest } from '../state/provenance-event-listing'; @@ -31,19 +31,6 @@ export class ProvenanceService { private nifiCommon: NiFiCommon ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - getSearchOptions(): Observable { return this.httpClient.get(`${ProvenanceService.API}/provenance/search-options`); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/queue/service/queue.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/queue/service/queue.service.ts index afdd3e763a..695d73ae87 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/queue/service/queue.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/queue/service/queue.service.ts @@ -30,25 +30,12 @@ export class QueueService { private nifiCommon: NiFiCommon ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - getConnection(connectionId: string): Observable { return this.httpClient.get(`${QueueService.API}/connections/${connectionId}`); } getFlowFile(flowfileSummary: FlowFileSummary): Observable { - return this.httpClient.get(this.stripProtocol(flowfileSummary.uri)); + return this.httpClient.get(this.nifiCommon.stripProtocol(flowfileSummary.uri)); } submitQueueListingRequest(queueListingRequest: SubmitQueueListingRequest): Observable { @@ -59,15 +46,15 @@ export class QueueService { } pollQueueListingRequest(listingRequest: ListingRequest): Observable { - return this.httpClient.get(this.stripProtocol(listingRequest.uri)); + return this.httpClient.get(this.nifiCommon.stripProtocol(listingRequest.uri)); } deleteQueueListingRequest(listingRequest: ListingRequest): Observable { - return this.httpClient.delete(this.stripProtocol(listingRequest.uri)); + return this.httpClient.delete(this.nifiCommon.stripProtocol(listingRequest.uri)); } downloadContent(flowfileSummary: FlowFileSummary): void { - let dataUri: string = `${this.stripProtocol(flowfileSummary.uri)}/content`; + let dataUri: string = `${this.nifiCommon.stripProtocol(flowfileSummary.uri)}/content`; const queryParameters: any = {}; @@ -83,7 +70,7 @@ export class QueueService { viewContent(flowfileSummary: FlowFileSummary, contentViewerUrl: string): void { // build the uri to the data - let dataUri: string = `${this.stripProtocol(flowfileSummary.uri)}/content`; + let dataUri: string = `${this.nifiCommon.stripProtocol(flowfileSummary.uri)}/content`; const dataUriParameters: any = {}; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts index 441a5bd06c..b60ccc93af 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings-routing.module.ts @@ -99,7 +99,26 @@ const routes: Routes = [ } ] }, - { path: 'parameter-providers', component: ParameterProviders } + { + path: 'parameter-providers', + component: ParameterProviders, + children: [ + { + path: ':id', + component: ParameterProviders, + children: [ + { + path: 'edit', + component: ParameterProviders + }, + { + path: 'fetch', + component: ParameterProviders + } + ] + } + ] + } ] } ]; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts index ba43f59442..c15b377c6e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/feature/settings.module.ts @@ -35,6 +35,7 @@ import { ReportingTasksEffects } from '../state/reporting-tasks/reporting-tasks. import { RegistryClientsEffects } from '../state/registry-clients/registry-clients.effects'; import { FlowAnalysisRulesEffects } from '../state/flow-analysis-rules/flow-analysis-rules.effects'; import { Navigation } from '../../../ui/common/navigation/navigation.component'; +import { ParameterProvidersEffects } from '../state/parameter-providers/parameter-providers.effects'; @NgModule({ declarations: [Settings], @@ -54,7 +55,8 @@ import { Navigation } from '../../../ui/common/navigation/navigation.component'; ManagementControllerServicesEffects, ReportingTasksEffects, FlowAnalysisRulesEffects, - RegistryClientsEffects + RegistryClientsEffects, + ParameterProvidersEffects ), MatTabsModule, Navigation diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts index b6f0a9ca14..c15ae5072c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/flow-analysis-rule.service.ts @@ -27,24 +27,12 @@ import { EnableFlowAnalysisRuleRequest, FlowAnalysisRuleEntity } from '../state/flow-analysis-rules'; +import { PropertyDescriptorRetriever } from '../../../state/shared'; @Injectable({ providedIn: 'root' }) -export class FlowAnalysisRuleService { +export class FlowAnalysisRuleService implements PropertyDescriptorRetriever { private static readonly API: string = '../nifi-api'; - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - constructor( private httpClient: HttpClient, private client: Client, @@ -68,7 +56,7 @@ export class FlowAnalysisRuleService { deleteFlowAnalysisRule(deleteFlowAnalysisRule: DeleteFlowAnalysisRuleRequest): Observable { const entity: FlowAnalysisRuleEntity = deleteFlowAnalysisRule.flowAnalysisRule; const revision: any = this.client.getRevision(entity); - return this.httpClient.delete(this.stripProtocol(entity.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision }); } getPropertyDescriptor(id: string, propertyName: string, sensitive: boolean): Observable { @@ -83,14 +71,14 @@ export class FlowAnalysisRuleService { updateFlowAnalysisRule(configureFlowAnalysisRule: ConfigureFlowAnalysisRuleRequest): Observable { return this.httpClient.put( - this.stripProtocol(configureFlowAnalysisRule.uri), + this.nifiCommon.stripProtocol(configureFlowAnalysisRule.uri), configureFlowAnalysisRule.payload ); } setEnable(flowAnalysisRule: EnableFlowAnalysisRuleRequest, enabled: boolean): Observable { const entity: FlowAnalysisRuleEntity = flowAnalysisRule.flowAnalysisRule; - return this.httpClient.put(`${this.stripProtocol(entity.uri)}/run-status`, { + return this.httpClient.put(`${this.nifiCommon.stripProtocol(entity.uri)}/run-status`, { revision: this.client.getRevision(entity), state: enabled ? 'ENABLED' : 'DISABLED', uiOnly: true diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/management-controller-service.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/management-controller-service.service.ts index e241e76544..9557f3d271 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/management-controller-service.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/management-controller-service.service.ts @@ -16,34 +16,25 @@ */ import { Injectable } from '@angular/core'; -import { Observable, throwError } from 'rxjs'; +import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { ConfigureControllerServiceRequest, - CreateControllerServiceRequest, DeleteControllerServiceRequest } from '../state/management-controller-services'; import { Client } from '../../../service/client.service'; import { NiFiCommon } from '../../../service/nifi-common.service'; -import { ControllerServiceEntity } from '../../../state/shared'; +import { + ControllerServiceCreator, + ControllerServiceEntity, + CreateControllerServiceRequest, + PropertyDescriptorRetriever +} from '../../../state/shared'; @Injectable({ providedIn: 'root' }) -export class ManagementControllerServiceService { +export class ManagementControllerServiceService implements ControllerServiceCreator, PropertyDescriptorRetriever { private static readonly API: string = '../nifi-api'; - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - constructor( private httpClient: HttpClient, private client: Client, @@ -79,7 +70,7 @@ export class ManagementControllerServiceService { updateControllerService(configureControllerService: ConfigureControllerServiceRequest): Observable { return this.httpClient.put( - this.stripProtocol(configureControllerService.uri), + this.nifiCommon.stripProtocol(configureControllerService.uri), configureControllerService.payload ); } @@ -87,6 +78,6 @@ export class ManagementControllerServiceService { deleteControllerService(deleteControllerService: DeleteControllerServiceRequest): Observable { const entity: ControllerServiceEntity = deleteControllerService.controllerService; const revision: any = this.client.getRevision(entity); - return this.httpClient.delete(this.stripProtocol(entity.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision }); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/parameter-provider.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/parameter-provider.service.ts new file mode 100644 index 0000000000..ed22e317c0 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/parameter-provider.service.ts @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Client } from '../../../service/client.service'; +import { NiFiCommon } from '../../../service/nifi-common.service'; +import { Observable } from 'rxjs'; +import { + ConfigureParameterProviderRequest, + CreateParameterProviderRequest, + DeleteParameterProviderRequest, + ParameterProviderEntity +} from '../state/parameter-providers'; +import { PropertyDescriptorRetriever, Revision } from '../../../state/shared'; + +@Injectable({ providedIn: 'root' }) +export class ParameterProviderService implements PropertyDescriptorRetriever { + private static readonly API: string = '../nifi-api'; + + constructor( + private httpClient: HttpClient, + private client: Client, + private nifiCommon: NiFiCommon + ) {} + + getParameterProviders(): Observable { + return this.httpClient.get(`${ParameterProviderService.API}/flow/parameter-providers`); + } + + createParameterProvider(request: CreateParameterProviderRequest) { + return this.httpClient.post(`${ParameterProviderService.API}/controller/parameter-providers`, { + revision: request.revision, + component: { + bundle: request.parameterProviderBundle, + type: request.parameterProviderType + } + }); + } + + deleteParameterProvider(request: DeleteParameterProviderRequest) { + const entity: ParameterProviderEntity = request.parameterProvider; + const revision: any = this.client.getRevision(entity); + const params: any = { + ...revision, + disconnectedNodeAcknowledged: false + }; + return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision }); + } + + getPropertyDescriptor(id: string, propertyName: string, sensitive: boolean): Observable { + const params: any = { + propertyName, + sensitive + }; + return this.httpClient.get(`${ParameterProviderService.API}/parameter-providers/${id}/descriptors`, { + params + }); + } + + updateParameterProvider(configureRequest: ConfigureParameterProviderRequest): Observable { + return this.httpClient.put(this.nifiCommon.stripProtocol(configureRequest.uri), configureRequest.payload); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/registry-client.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/registry-client.service.ts index b76e08b453..c1aeab3473 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/registry-client.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/registry-client.service.ts @@ -26,24 +26,12 @@ import { EditRegistryClientRequest, RegistryClientEntity } from '../state/registry-clients'; +import { PropertyDescriptorRetriever } from '../../../state/shared'; @Injectable({ providedIn: 'root' }) -export class RegistryClientService { +export class RegistryClientService implements PropertyDescriptorRetriever { private static readonly API: string = '../nifi-api'; - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - constructor( private httpClient: HttpClient, private client: Client, @@ -69,12 +57,12 @@ export class RegistryClientService { } updateRegistryClient(request: EditRegistryClientRequest): Observable { - return this.httpClient.put(this.stripProtocol(request.uri), request.payload); + return this.httpClient.put(this.nifiCommon.stripProtocol(request.uri), request.payload); } deleteRegistryClient(deleteRegistryClient: DeleteRegistryClientRequest): Observable { const entity: RegistryClientEntity = deleteRegistryClient.registryClient; const revision: any = this.client.getRevision(entity); - return this.httpClient.delete(this.stripProtocol(entity.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision }); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/reporting-task.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/reporting-task.service.ts index 102fab05c5..d6358e6a3e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/reporting-task.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/service/reporting-task.service.ts @@ -28,24 +28,12 @@ import { StartReportingTaskRequest, StopReportingTaskRequest } from '../state/reporting-tasks'; +import { PropertyDescriptorRetriever } from '../../../state/shared'; @Injectable({ providedIn: 'root' }) -export class ReportingTaskService { +export class ReportingTaskService implements PropertyDescriptorRetriever { private static readonly API: string = '../nifi-api'; - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - constructor( private httpClient: HttpClient, private client: Client, @@ -69,7 +57,7 @@ export class ReportingTaskService { deleteReportingTask(deleteReportingTask: DeleteReportingTaskRequest): Observable { const entity: ReportingTaskEntity = deleteReportingTask.reportingTask; const revision: any = this.client.getRevision(entity); - return this.httpClient.delete(this.stripProtocol(entity.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(entity.uri), { params: revision }); } startReportingTask(startReportingTask: StartReportingTaskRequest): Observable { @@ -79,7 +67,7 @@ export class ReportingTaskService { revision, state: 'RUNNING' }; - return this.httpClient.put(`${this.stripProtocol(entity.uri)}/run-status`, payload); + return this.httpClient.put(`${this.nifiCommon.stripProtocol(entity.uri)}/run-status`, payload); } stopReportingTask(stopReportingTask: StopReportingTaskRequest): Observable { @@ -89,7 +77,7 @@ export class ReportingTaskService { revision, state: 'STOPPED' }; - return this.httpClient.put(`${this.stripProtocol(entity.uri)}/run-status`, payload); + return this.httpClient.put(`${this.nifiCommon.stripProtocol(entity.uri)}/run-status`, payload); } getPropertyDescriptor(id: string, propertyName: string, sensitive: boolean): Observable { @@ -103,6 +91,9 @@ export class ReportingTaskService { } updateReportingTask(configureReportingTask: ConfigureReportingTaskRequest): Observable { - return this.httpClient.put(this.stripProtocol(configureReportingTask.uri), configureReportingTask.payload); + return this.httpClient.put( + this.nifiCommon.stripProtocol(configureReportingTask.uri), + configureReportingTask.payload + ); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts index dadda56080..57d734a6cb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/flow-analysis-rules/flow-analysis-rules.effects.ts @@ -18,7 +18,7 @@ import { Injectable } from '@angular/core'; import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import * as FlowAnalysisRuleActions from './flow-analysis-rules.actions'; -import { catchError, from, map, NEVER, Observable, of, switchMap, take, takeUntil, tap } from 'rxjs'; +import { catchError, from, map, of, switchMap, take, takeUntil, tap } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { NiFiState } from '../../../../state'; @@ -30,20 +30,11 @@ import { ManagementControllerServiceService } from '../../service/management-con import { CreateFlowAnalysisRule } from '../../ui/flow-analysis-rules/create-flow-analysis-rule/create-flow-analysis-rule.component'; import { Router } from '@angular/router'; import { selectSaving } from '../management-controller-services/management-controller-services.selectors'; -import { - InlineServiceCreationRequest, - InlineServiceCreationResponse, - NewPropertyDialogRequest, - NewPropertyDialogResponse, - Property, - PropertyDescriptor, - UpdateControllerServiceRequest -} from '../../../../state/shared'; +import { UpdateControllerServiceRequest } from '../../../../state/shared'; import { EditFlowAnalysisRule } from '../../ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component'; import { CreateFlowAnalysisRuleSuccess } from './index'; -import { NewPropertyDialog } from '../../../../ui/common/new-property-dialog/new-property-dialog.component'; -import { CreateControllerService } from '../../../../ui/common/controller-service/create-controller-service/create-controller-service.component'; import { ExtensionTypesService } from '../../../../service/extension-types.service'; +import { PropertyTableHelperService } from '../../../../service/property-table-helper.service'; @Injectable() export class FlowAnalysisRulesEffects { @@ -55,7 +46,8 @@ export class FlowAnalysisRulesEffects { private extensionTypesService: ExtensionTypesService, private flowAnalysisRuleService: FlowAnalysisRuleService, private dialog: MatDialog, - private router: Router + private router: Router, + private propertyTableHelperService: PropertyTableHelperService ) {} loadFlowAnalysisRule$ = createEffect(() => @@ -225,36 +217,8 @@ export class FlowAnalysisRulesEffects { editDialogReference.componentInstance.saving$ = this.store.select(selectSaving); - editDialogReference.componentInstance.createNewProperty = ( - existingProperties: string[], - allowsSensitive: boolean - ): Observable => { - const dialogRequest: NewPropertyDialogRequest = { existingProperties, allowsSensitive }; - const newPropertyDialogReference = this.dialog.open(NewPropertyDialog, { - data: dialogRequest, - panelClass: 'small-dialog' - }); - - return newPropertyDialogReference.componentInstance.newProperty.pipe( - take(1), - switchMap((dialogResponse: NewPropertyDialogResponse) => { - return this.flowAnalysisRuleService - .getPropertyDescriptor(request.id, dialogResponse.name, dialogResponse.sensitive) - .pipe( - take(1), - map((response) => { - newPropertyDialogReference.close(); - - return { - property: dialogResponse.name, - value: null, - descriptor: response.propertyDescriptor - }; - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewProperty = + this.propertyTableHelperService.createNewProperty(request.id, this.flowAnalysisRuleService); const goTo = (commands: string[], destination: string): void => { if (editDialogReference.componentInstance.editFlowAnalysisRuleForm.dirty) { @@ -285,73 +249,12 @@ export class FlowAnalysisRulesEffects { goTo(commands, 'Controller Service'); }; - editDialogReference.componentInstance.createNewService = ( - request: InlineServiceCreationRequest - ): Observable => { - const descriptor: PropertyDescriptor = request.descriptor; - - // fetch all services that implement the requested service api - return this.extensionTypesService - .getImplementingControllerServiceTypes( - // @ts-ignore - descriptor.identifiesControllerService, - descriptor.identifiesControllerServiceBundle - ) - .pipe( - take(1), - switchMap((implementingTypesResponse) => { - // show the create controller service dialog with the types that implemented the interface - const createServiceDialogReference = this.dialog.open(CreateControllerService, { - data: { - controllerServiceTypes: implementingTypesResponse.controllerServiceTypes - }, - panelClass: 'medium-dialog' - }); - - return createServiceDialogReference.componentInstance.createControllerService.pipe( - take(1), - switchMap((controllerServiceType) => { - // typically this sequence would be implemented with ngrx actions, however we are - // currently in an edit session and we need to return both the value (new service id) - // and updated property descriptor so the table renders correctly - return this.managementControllerServiceService - .createControllerService({ - revision: { - clientId: this.client.getClientId(), - version: 0 - }, - controllerServiceType: controllerServiceType.type, - controllerServiceBundle: controllerServiceType.bundle - }) - .pipe( - take(1), - switchMap((createResponse) => { - // fetch an updated property descriptor - return this.flowAnalysisRuleService - .getPropertyDescriptor(ruleId, descriptor.name, false) - .pipe( - take(1), - map((descriptorResponse) => { - createServiceDialogReference.close(); - - return { - value: createResponse.id, - descriptor: - descriptorResponse.propertyDescriptor - }; - }) - ); - }), - catchError((error) => { - // TODO - show error - return NEVER; - }) - ); - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewService = + this.propertyTableHelperService.createNewService( + request.id, + this.managementControllerServiceService, + this.flowAnalysisRuleService + ); editDialogReference.componentInstance.editFlowAnalysisRule .pipe(takeUntil(editDialogReference.afterClosed())) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts index 1182b01024..9367df4e90 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/index.ts @@ -29,6 +29,8 @@ import { registryClientsFeatureKey, RegistryClientsState } from './registry-clie import { registryClientsReducer } from './registry-clients/registry-clients.reducer'; import { flowAnalysisRulesFeatureKey, FlowAnalysisRulesState } from './flow-analysis-rules'; import { flowAnalysisRulesReducer } from './flow-analysis-rules/flow-analysis-rules.reducer'; +import { parameterProvidersFeatureKey, ParameterProvidersState } from './parameter-providers'; +import { parameterProvidersReducer } from './parameter-providers/parameter-providers.reducer'; export const settingsFeatureKey = 'settings'; @@ -38,6 +40,7 @@ export interface SettingsState { [reportingTasksFeatureKey]: ReportingTasksState; [flowAnalysisRulesFeatureKey]: FlowAnalysisRulesState; [registryClientsFeatureKey]: RegistryClientsState; + [parameterProvidersFeatureKey]: ParameterProvidersState; } export function reducers(state: SettingsState | undefined, action: Action) { @@ -46,7 +49,8 @@ export function reducers(state: SettingsState | undefined, action: Action) { [managementControllerServicesFeatureKey]: managementControllerServicesReducer, [reportingTasksFeatureKey]: reportingTasksReducer, [flowAnalysisRulesFeatureKey]: flowAnalysisRulesReducer, - [registryClientsFeatureKey]: registryClientsReducer + [registryClientsFeatureKey]: registryClientsReducer, + [parameterProvidersFeatureKey]: parameterProvidersReducer })(state, action); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/index.ts index 9db8417be2..b1725d338c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/index.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/index.ts @@ -24,12 +24,6 @@ export interface LoadManagementControllerServicesResponse { loadedTimestamp: string; } -export interface CreateControllerServiceRequest { - controllerServiceType: string; - controllerServiceBundle: Bundle; - revision: Revision; -} - export interface CreateControllerServiceSuccess { controllerService: ControllerServiceEntity; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.actions.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.actions.ts index 7165d17de7..0ecaca9f51 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.actions.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.actions.ts @@ -19,7 +19,6 @@ import { createAction, props } from '@ngrx/store'; import { ConfigureControllerServiceRequest, ConfigureControllerServiceSuccess, - CreateControllerServiceRequest, CreateControllerServiceSuccess, DeleteControllerServiceRequest, DeleteControllerServiceSuccess, @@ -27,6 +26,7 @@ import { SelectControllerServiceRequest } from './index'; import { + CreateControllerServiceRequest, DisableControllerServiceDialogRequest, EditControllerServiceDialogRequest, SetEnableControllerServiceDialogRequest diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.effects.ts index 2e9ff9772e..62abf521cb 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/management-controller-services/management-controller-services.effects.ts @@ -33,18 +33,15 @@ import { ControllerServiceReferencingComponent, InlineServiceCreationRequest, InlineServiceCreationResponse, - NewPropertyDialogRequest, - NewPropertyDialogResponse, - Property, PropertyDescriptor, UpdateControllerServiceRequest } from '../../../../state/shared'; -import { NewPropertyDialog } from '../../../../ui/common/new-property-dialog/new-property-dialog.component'; import { Router } from '@angular/router'; import { ExtensionTypesService } from '../../../../service/extension-types.service'; import { selectSaving } from './management-controller-services.selectors'; import { EnableControllerService } from '../../../../ui/common/controller-service/enable-controller-service/enable-controller-service.component'; import { DisableControllerService } from '../../../../ui/common/controller-service/disable-controller-service/disable-controller-service.component'; +import { PropertyTableHelperService } from '../../../../service/property-table-helper.service'; @Injectable() export class ManagementControllerServicesEffects { @@ -55,7 +52,8 @@ export class ManagementControllerServicesEffects { private managementControllerServiceService: ManagementControllerServiceService, private extensionTypesService: ExtensionTypesService, private dialog: MatDialog, - private router: Router + private router: Router, + private propertyTableHelperService: PropertyTableHelperService ) {} loadManagementControllerServices$ = createEffect(() => @@ -193,36 +191,11 @@ export class ManagementControllerServicesEffects { editDialogReference.componentInstance.saving$ = this.store.select(selectSaving); - editDialogReference.componentInstance.createNewProperty = ( - existingProperties: string[], - allowsSensitive: boolean - ): Observable => { - const dialogRequest: NewPropertyDialogRequest = { existingProperties, allowsSensitive }; - const newPropertyDialogReference = this.dialog.open(NewPropertyDialog, { - data: dialogRequest, - panelClass: 'small-dialog' - }); - - return newPropertyDialogReference.componentInstance.newProperty.pipe( - take(1), - switchMap((dialogResponse: NewPropertyDialogResponse) => { - return this.managementControllerServiceService - .getPropertyDescriptor(request.id, dialogResponse.name, dialogResponse.sensitive) - .pipe( - take(1), - map((response) => { - newPropertyDialogReference.close(); - - return { - property: dialogResponse.name, - value: null, - descriptor: response.propertyDescriptor - }; - }) - ); - }) + editDialogReference.componentInstance.createNewProperty = + this.propertyTableHelperService.createNewProperty( + request.id, + this.managementControllerServiceService ); - }; const goTo = (commands: string[], destination: string): void => { if (editDialogReference.componentInstance.editControllerServiceForm.dirty) { @@ -260,84 +233,21 @@ export class ManagementControllerServicesEffects { goTo(route, component.referenceType); }; - editDialogReference.componentInstance.createNewService = ( - request: InlineServiceCreationRequest - ): Observable => { - const descriptor: PropertyDescriptor = request.descriptor; - - // fetch all services that implement the requested service api - return this.extensionTypesService - .getImplementingControllerServiceTypes( - // @ts-ignore - descriptor.identifiesControllerService, - descriptor.identifiesControllerServiceBundle - ) - .pipe( - take(1), - switchMap((implementingTypesResponse) => { - // show the create controller service dialog with the types that implemented the interface - const createServiceDialogReference = this.dialog.open(CreateControllerService, { - data: { - controllerServiceTypes: implementingTypesResponse.controllerServiceTypes - }, - panelClass: 'medium-dialog' - }); - - return createServiceDialogReference.componentInstance.createControllerService.pipe( - take(1), - switchMap((controllerServiceType) => { - // typically this sequence would be implemented with ngrx actions, however we are - // currently in an edit session and we need to return both the value (new service id) - // and updated property descriptor so the table renders correctly - return this.managementControllerServiceService - .createControllerService({ - revision: { - clientId: this.client.getClientId(), - version: 0 - }, - controllerServiceType: controllerServiceType.type, - controllerServiceBundle: controllerServiceType.bundle - }) - .pipe( - take(1), - switchMap((createResponse) => { - // dispatch an inline create service success action so the new service is in the state - this.store.dispatch( - ManagementControllerServicesActions.inlineCreateControllerServiceSuccess( - { - response: { - controllerService: createResponse - } - } - ) - ); - - // fetch an updated property descriptor - return this.managementControllerServiceService - .getPropertyDescriptor(serviceId, descriptor.name, false) - .pipe( - take(1), - map((descriptorResponse) => { - createServiceDialogReference.close(); - - return { - value: createResponse.id, - descriptor: - descriptorResponse.propertyDescriptor - }; - }) - ); - }), - catchError((error) => { - // TODO - show error - return NEVER; - }) - ); - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewService = + this.propertyTableHelperService.createNewService( + request.id, + this.managementControllerServiceService, + this.managementControllerServiceService, + null, + (createResponse) => + this.store.dispatch( + ManagementControllerServicesActions.inlineCreateControllerServiceSuccess({ + response: { + controllerService: createResponse + } + }) + ) + ); editDialogReference.componentInstance.editControllerService .pipe(takeUntil(editDialogReference.afterClosed())) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/index.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/index.ts new file mode 100644 index 0000000000..957951e435 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/index.ts @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Bundle, + DocumentedType, + ParameterContextReferenceEntity, + Permissions, + PropertyDescriptor, + Revision +} from '../../../../state/shared'; + +export const parameterProvidersFeatureKey = 'parameterProviders'; + +export interface ParameterProvider { + bundle: Bundle; + comments: string; + deprecated: boolean; + descriptors: { [key: string]: PropertyDescriptor }; + extensionMissing: boolean; + id: string; + multipleVersionsAvailable: boolean; + name: string; + parameterGroupConfigurations: any[]; + persistsState: boolean; + properties: { [key: string]: string }; + referencingParameterContexts: ParameterContextReferenceEntity[]; + restricted: boolean; + type: string; + validationStatus: string; + validationErrors?: string[]; +} + +export interface ParameterProviderEntity { + id: string; + bulletins: []; + component: ParameterProvider; + permissions: Permissions; + revision: Revision; + uri: string; +} + +export interface ParameterProvidersState { + parameterProviders: ParameterProviderEntity[]; + saving: boolean; + loadedTimestamp: string; + error: string | null; + status: 'pending' | 'loading' | 'error' | 'success'; +} + +export interface LoadParameterProvidersResponse { + parameterProviders: ParameterProviderEntity[]; + loadedTimestamp: string; +} + +export interface SelectParameterProviderRequest { + id: string; +} + +export interface CreateParameterProviderDialogRequest { + parameterProviderTypes: DocumentedType[]; +} + +export interface CreateParameterProviderRequest { + parameterProviderType: string; + parameterProviderBundle: Bundle; + revision: Revision; +} + +export interface CreateParameterProviderSuccessResponse { + parameterProvider: ParameterProviderEntity; +} + +export interface DeleteParameterProviderRequest { + parameterProvider: ParameterProviderEntity; +} + +export interface DeleteParameterProviderSuccess { + parameterProvider: ParameterProviderEntity; +} + +export interface EditParameterProviderRequest { + id: string; + parameterProvider: ParameterProviderEntity; +} + +export interface ConfigureParameterProviderRequest { + id: string; + uri: string; + payload: any; + postUpdateNavigation?: string[]; +} + +export interface ConfigureParameterProviderSuccess { + id: string; + parameterProvider: ParameterProviderEntity; + postUpdateNavigation?: string[]; +} + +export interface UpdateParameterProviderRequest { + payload: any; + postUpdateNavigation?: string[]; +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.actions.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.actions.ts new file mode 100644 index 0000000000..ae7166e57e --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.actions.ts @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createAction, props } from '@ngrx/store'; +import { + ConfigureParameterProviderRequest, + ConfigureParameterProviderSuccess, + CreateParameterProviderRequest, + CreateParameterProviderSuccessResponse, + DeleteParameterProviderRequest, + DeleteParameterProviderSuccess, + EditParameterProviderRequest, + LoadParameterProvidersResponse, + SelectParameterProviderRequest +} from './index'; + +const PARAMETER_PROVIDERS_PREFIX: string = '[Parameter Providers]'; + +export const resetParameterProvidersState = createAction(`${PARAMETER_PROVIDERS_PREFIX} Reset Parameter Providers`); + +export const loadParameterProviders = createAction(`${PARAMETER_PROVIDERS_PREFIX} Load Parameter Providers`); + +export const loadParameterProvidersSuccess = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Load Parameter Providers Success`, + props<{ response: LoadParameterProvidersResponse }>() +); + +export const parameterProvidersApiError = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Load Parameter Providers Error`, + props<{ error: string }>() +); + +export const selectParameterProvider = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Select Parameter Provider`, + props<{ request: SelectParameterProviderRequest }>() +); + +export const openNewParameterProviderDialog = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Open New Parameter Provider Dialog` +); + +export const createParameterProvider = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Create Parameter Provider`, + props<{ request: CreateParameterProviderRequest }>() +); + +export const createParameterProviderSuccess = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Create Parameter Provider Success`, + props<{ response: CreateParameterProviderSuccessResponse }>() +); + +export const promptParameterProviderDeletion = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Prompt Parameter Provider Deletion`, + props<{ request: DeleteParameterProviderRequest }>() +); + +export const deleteParameterProvider = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Delete Parameter Provider`, + props<{ request: DeleteParameterProviderRequest }>() +); + +export const deleteParameterProviderSuccess = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Delete Parameter Provider Success`, + props<{ response: DeleteParameterProviderSuccess }>() +); + +export const navigateToEditParameterProvider = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Navigate To Edit Parameter Provider`, + props<{ id: string }>() +); + +export const openConfigureParameterProviderDialog = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Open Configure Parameter Provider Dialog`, + props<{ request: EditParameterProviderRequest }>() +); + +export const configureParameterProvider = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Configure Parameter Provider`, + props<{ request: ConfigureParameterProviderRequest }>() +); + +export const configureParameterProviderSuccess = createAction( + `${PARAMETER_PROVIDERS_PREFIX} Configure Parameter Provider Success`, + props<{ response: ConfigureParameterProviderSuccess }>() +); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.effects.ts new file mode 100644 index 0000000000..67cbdbfce1 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.effects.ts @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; +import { Store } from '@ngrx/store'; +import { NiFiState } from '../../../../state'; +import { Client } from '../../../../service/client.service'; +import { MatDialog } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { ParameterProviderService } from '../../service/parameter-provider.service'; +import * as ParameterProviderActions from './parameter-providers.actions'; +import { loadParameterProviders, selectParameterProvider } from './parameter-providers.actions'; +import { catchError, from, map, of, switchMap, take, takeUntil, tap } from 'rxjs'; +import { selectSaving } from './parameter-providers.selectors'; +import { selectParameterProviderTypes } from '../../../../state/extension-types/extension-types.selectors'; +import { CreateParameterProvider } from '../../ui/parameter-providers/create-parameter-provider/create-parameter-provider.component'; +import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component'; +import { EditParameterProvider } from '../../ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component'; +import { PropertyTableHelperService } from '../../../../service/property-table-helper.service'; +import { UpdateParameterProviderRequest } from './index'; +import { ManagementControllerServiceService } from '../../service/management-controller-service.service'; + +@Injectable() +export class ParameterProvidersEffects { + constructor( + private actions$: Actions, + private store: Store, + private client: Client, + private dialog: MatDialog, + private router: Router, + private parameterProviderService: ParameterProviderService, + private propertyTableHelperService: PropertyTableHelperService, + private managementControllerServiceService: ManagementControllerServiceService + ) {} + + loadParameterProviders$ = createEffect(() => + this.actions$.pipe( + ofType(loadParameterProviders), + switchMap(() => + from(this.parameterProviderService.getParameterProviders()).pipe( + map((response) => + ParameterProviderActions.loadParameterProvidersSuccess({ + response: { + parameterProviders: response.parameterProviders, + loadedTimestamp: response.currentTime + } + }) + ), + catchError((error) => + of(ParameterProviderActions.parameterProvidersApiError({ error: error.error })) + ) + ) + ) + ) + ); + + selectParameterProvider$ = createEffect( + () => + this.actions$.pipe( + ofType(selectParameterProvider), + map((action) => action.request), + tap((request) => { + this.router.navigate(['/settings', 'parameter-providers', request.id]); + }) + ), + { dispatch: false } + ); + + openNewParameterProviderDialog$ = createEffect( + () => + this.actions$.pipe( + ofType(ParameterProviderActions.openNewParameterProviderDialog), + concatLatestFrom(() => this.store.select(selectParameterProviderTypes)), + tap(([action, parameterProviderTypes]) => { + const dialogReference = this.dialog.open(CreateParameterProvider, { + data: { + parameterProviderTypes + }, + panelClass: 'medium-dialog' + }); + + dialogReference.componentInstance.saving$ = this.store.select(selectSaving); + + dialogReference.componentInstance.createParameterProvider + .pipe(take(1)) + .subscribe((parameterProviderType) => { + this.store.dispatch( + ParameterProviderActions.createParameterProvider({ + request: { + parameterProviderType: parameterProviderType.type, + parameterProviderBundle: parameterProviderType.bundle, + revision: { + clientId: this.client.getClientId(), + version: 0 + } + } + }) + ); + }); + }) + ), + { dispatch: false } + ); + + createParameterProvider$ = createEffect(() => + this.actions$.pipe( + ofType(ParameterProviderActions.createParameterProvider), + map((action) => action.request), + switchMap((request) => + from(this.parameterProviderService.createParameterProvider(request)).pipe( + map((response: any) => + ParameterProviderActions.createParameterProviderSuccess({ + response: { + parameterProvider: response + } + }) + ), + catchError((error) => + of(ParameterProviderActions.parameterProvidersApiError({ error: error.error })) + ) + ) + ) + ) + ); + + createParameterProviderSuccess$ = createEffect(() => + this.actions$.pipe( + ofType(ParameterProviderActions.createParameterProviderSuccess), + map((action) => action.response), + tap(() => { + this.dialog.closeAll(); + }), + switchMap((response) => + of( + ParameterProviderActions.selectParameterProvider({ + request: { + id: response.parameterProvider.id + } + }) + ) + ) + ) + ); + + promptParameterProviderDeletion$ = createEffect( + () => + this.actions$.pipe( + ofType(ParameterProviderActions.promptParameterProviderDeletion), + map((action) => action.request), + tap((request) => { + const dialogReference = this.dialog.open(YesNoDialog, { + data: { + title: 'Delete Parameter Provider', + message: `Delete parameter provider ${request.parameterProvider.component.name}?` + }, + panelClass: 'small-dialog' + }); + + dialogReference.componentInstance.yes.pipe(take(1)).subscribe(() => + this.store.dispatch( + ParameterProviderActions.deleteParameterProvider({ + request + }) + ) + ); + }) + ), + { dispatch: false } + ); + + deleteParameterProvider = createEffect(() => + this.actions$.pipe( + ofType(ParameterProviderActions.deleteParameterProvider), + map((action) => action.request), + switchMap((request) => + from(this.parameterProviderService.deleteParameterProvider(request)).pipe( + map((response: any) => + ParameterProviderActions.deleteParameterProviderSuccess({ + response: { + parameterProvider: response + } + }) + ), + catchError((error) => + of( + ParameterProviderActions.parameterProvidersApiError({ + error: error.error + }) + ) + ) + ) + ) + ) + ); + + navigateToEditParameterProvider$ = createEffect( + () => + this.actions$.pipe( + ofType(ParameterProviderActions.navigateToEditParameterProvider), + map((action) => action.id), + tap((id) => { + this.router.navigate(['settings', 'parameter-providers', id, 'edit']); + }) + ), + { dispatch: false } + ); + + openConfigureParameterProviderDialog$ = createEffect( + () => + this.actions$.pipe( + ofType(ParameterProviderActions.openConfigureParameterProviderDialog), + map((action) => action.request), + tap((request) => { + const id = request.id; + const editDialogReference = this.dialog.open(EditParameterProvider, { + data: { + parameterProvider: request.parameterProvider + }, + id, + panelClass: 'large-dialog' + }); + + editDialogReference.componentInstance.saving$ = this.store.select(selectSaving); + + const goTo = (commands: string[], destination: string) => { + // confirm navigating away while changes are unsaved + if (editDialogReference.componentInstance.editParameterProviderForm.dirty) { + const promptSaveDialogRef = this.dialog.open(YesNoDialog, { + data: { + title: 'Parameter Provider Configuration', + message: `Save changes before going to this ${destination}` + }, + panelClass: 'small-dialog' + }); + + promptSaveDialogRef.componentInstance.yes.pipe(take(1)).subscribe(() => { + editDialogReference.componentInstance.submitForm(commands); + }); + + promptSaveDialogRef.componentInstance.no.pipe(take(1)).subscribe(() => { + editDialogReference.close('ROUTED'); + this.router.navigate(commands); + }); + } else { + editDialogReference.close('ROUTED'); + this.router.navigate(commands); + } + }; + + editDialogReference.componentInstance.goToReferencingParameterContext = (id: string) => { + const commands: string[] = ['parameter-contexts', id]; + goTo(commands, 'Parameter Context'); + }; + + editDialogReference.componentInstance.goToService = (serviceId: string) => { + const commands: string[] = ['/settings', 'management-controller-services', serviceId]; + goTo(commands, 'Controller Service'); + }; + + editDialogReference.componentInstance.createNewProperty = + this.propertyTableHelperService.createNewProperty(request.id, this.parameterProviderService); + + editDialogReference.componentInstance.createNewService = + this.propertyTableHelperService.createNewService( + request.id, + this.managementControllerServiceService, + this.parameterProviderService + ); + + editDialogReference.componentInstance.editParameterProvider + .pipe(takeUntil(editDialogReference.afterClosed())) + .subscribe((updateRequest: UpdateParameterProviderRequest) => { + this.store.dispatch( + ParameterProviderActions.configureParameterProvider({ + request: { + id: request.parameterProvider.id, + uri: request.parameterProvider.uri, + payload: updateRequest.payload, + postUpdateNavigation: updateRequest.postUpdateNavigation + } + }) + ); + }); + + editDialogReference.afterClosed().subscribe((response) => { + if (response !== 'ROUTED') { + this.store.dispatch( + ParameterProviderActions.selectParameterProvider({ + request: { + id + } + }) + ); + } + }); + }) + ), + { dispatch: false } + ); + + configureParameterProvider$ = createEffect(() => + this.actions$.pipe( + ofType(ParameterProviderActions.configureParameterProvider), + map((action) => action.request), + switchMap((request) => + from(this.parameterProviderService.updateParameterProvider(request)).pipe( + map((response) => + ParameterProviderActions.configureParameterProviderSuccess({ + response: { + id: request.id, + parameterProvider: response, + postUpdateNavigation: request.postUpdateNavigation + } + }) + ), + catchError((error) => + of( + ParameterProviderActions.parameterProvidersApiError({ + error: error.error + }) + ) + ) + ) + ) + ) + ); + + configureParameterProviderSuccess$ = createEffect( + () => + this.actions$.pipe( + ofType(ParameterProviderActions.configureParameterProviderSuccess), + map((action) => action.response), + tap((response) => { + if (response.postUpdateNavigation) { + this.router.navigate(response.postUpdateNavigation); + this.dialog.getDialogById(response.id)?.close('ROUTED'); + } else { + this.dialog.closeAll(); + } + }) + ), + { dispatch: false } + ); +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.reducer.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.reducer.ts new file mode 100644 index 0000000000..574fcffd39 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.reducer.ts @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ParameterProvidersState } from './index'; +import { createReducer, on } from '@ngrx/store'; +import { + configureParameterProvider, + configureParameterProviderSuccess, + createParameterProvider, + createParameterProviderSuccess, + deleteParameterProvider, + deleteParameterProviderSuccess, + loadParameterProviders, + loadParameterProvidersSuccess, + parameterProvidersApiError, + resetParameterProvidersState +} from './parameter-providers.actions'; +import { produce } from 'immer'; + +export const initialParameterProvidersState: ParameterProvidersState = { + parameterProviders: [], + saving: false, + loadedTimestamp: '', + error: null, + status: 'pending' +}; + +export const parameterProvidersReducer = createReducer( + initialParameterProvidersState, + + on(resetParameterProvidersState, (state: ParameterProvidersState) => ({ + ...initialParameterProvidersState + })), + + on(loadParameterProviders, (state: ParameterProvidersState) => ({ + ...state, + status: 'loading' as const + })), + + on(loadParameterProvidersSuccess, (state: ParameterProvidersState, { response }) => ({ + ...state, + parameterProviders: response.parameterProviders, + loadedTimestamp: response.loadedTimestamp, + error: null, + status: 'success' as const + })), + + on(parameterProvidersApiError, (state: ParameterProvidersState, { error }) => ({ + ...state, + saving: false, + error, + status: 'error' as const + })), + + on(createParameterProvider, configureParameterProvider, deleteParameterProvider, (state, { request }) => ({ + ...state, + saving: true + })), + + on(createParameterProviderSuccess, (state, { response }) => { + return produce(state, (draftState) => { + draftState.parameterProviders.push(response.parameterProvider); + draftState.saving = false; + }); + }), + + on(deleteParameterProviderSuccess, (state, { response }) => { + return produce(state, (draftState) => { + const idx = draftState.parameterProviders.findIndex( + (provider) => provider.id === response.parameterProvider.id + ); + if (idx > -1) { + draftState.parameterProviders.splice(idx, 1); + } + draftState.saving = false; + }); + }), + + on(configureParameterProviderSuccess, (state, { response }) => { + return produce(state, (draftState) => { + const idx = draftState.parameterProviders.findIndex( + (provider) => provider.id === response.parameterProvider.id + ); + + if (idx > -1) { + draftState.parameterProviders[idx] = response.parameterProvider; + } + + draftState.saving = false; + }); + }) +); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.selectors.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.selectors.ts new file mode 100644 index 0000000000..0afa75b379 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/parameter-providers/parameter-providers.selectors.ts @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createSelector } from '@ngrx/store'; +import { selectSettingsState, SettingsState } from '../index'; +import { ParameterProviderEntity, parameterProvidersFeatureKey, ParameterProvidersState } from './index'; +import { selectCurrentRoute } from '../../../../state/router/router.selectors'; + +export const selectParameterProvidersState = createSelector( + selectSettingsState, + (state: SettingsState) => state[parameterProvidersFeatureKey] +); + +export const selectSaving = createSelector( + selectParameterProvidersState, + (state: ParameterProvidersState) => state.saving +); + +export const selectParameterProviderIdFromRoute = createSelector(selectCurrentRoute, (route) => { + if (route) { + // always select the parameter provider from the route + return route.params.id; + } + return null; +}); + +export const selectSingleEditedParameterProvider = createSelector(selectCurrentRoute, (route) => { + if (route?.routeConfig?.path == 'edit') { + return route.params.id; + } + return null; +}); + +export const selectParameterProviders = createSelector( + selectParameterProvidersState, + (state: ParameterProvidersState) => state.parameterProviders +); + +export const selectParameterProvider = (id: string) => + createSelector(selectParameterProviders, (entities: ParameterProviderEntity[]) => + entities.find((entity) => id == entity.id) + ); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/registry-clients/registry-clients.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/registry-clients/registry-clients.effects.ts index 37eeb8e480..bd431167f1 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/registry-clients/registry-clients.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/registry-clients/registry-clients.effects.ts @@ -18,7 +18,7 @@ import { Injectable } from '@angular/core'; import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects'; import * as RegistryClientsActions from './registry-clients.actions'; -import { catchError, from, map, NEVER, Observable, of, switchMap, take, takeUntil, tap } from 'rxjs'; +import { catchError, from, map, of, switchMap, take, takeUntil, tap } from 'rxjs'; import { MatDialog } from '@angular/material/dialog'; import { Store } from '@ngrx/store'; import { NiFiState } from '../../../../state'; @@ -29,20 +29,11 @@ import { RegistryClientService } from '../../service/registry-client.service'; import { CreateRegistryClient } from '../../ui/registry-clients/create-registry-client/create-registry-client.component'; import { selectSaving } from './registry-clients.selectors'; import { EditRegistryClient } from '../../ui/registry-clients/edit-registry-client/edit-registry-client.component'; -import { - InlineServiceCreationRequest, - InlineServiceCreationResponse, - NewPropertyDialogRequest, - NewPropertyDialogResponse, - Property, - PropertyDescriptor -} from '../../../../state/shared'; -import { NewPropertyDialog } from '../../../../ui/common/new-property-dialog/new-property-dialog.component'; import { ExtensionTypesService } from '../../../../service/extension-types.service'; -import { CreateControllerService } from '../../../../ui/common/controller-service/create-controller-service/create-controller-service.component'; import { ManagementControllerServiceService } from '../../service/management-controller-service.service'; import { Client } from '../../../../service/client.service'; import { EditRegistryClientRequest } from './index'; +import { PropertyTableHelperService } from '../../../../service/property-table-helper.service'; @Injectable() export class RegistryClientsEffects { @@ -54,7 +45,8 @@ export class RegistryClientsEffects { private extensionTypesService: ExtensionTypesService, private managementControllerServiceService: ManagementControllerServiceService, private dialog: MatDialog, - private router: Router + private router: Router, + private propertyTableHelperService: PropertyTableHelperService ) {} loadRegistryClients$ = createEffect(() => @@ -181,40 +173,8 @@ export class RegistryClientsEffects { editDialogReference.componentInstance.saving$ = this.store.select(selectSaving); - editDialogReference.componentInstance.createNewProperty = ( - existingProperties: string[], - allowsSensitive: boolean - ): Observable => { - const dialogRequest: NewPropertyDialogRequest = { existingProperties, allowsSensitive }; - const newPropertyDialogReference = this.dialog.open(NewPropertyDialog, { - data: dialogRequest, - panelClass: 'small-dialog' - }); - - return newPropertyDialogReference.componentInstance.newProperty.pipe( - take(1), - switchMap((dialogResponse: NewPropertyDialogResponse) => { - return this.registryClientService - .getPropertyDescriptor( - registryClientId, - dialogResponse.name, - dialogResponse.sensitive - ) - .pipe( - take(1), - map((response) => { - newPropertyDialogReference.close(); - - return { - property: dialogResponse.name, - value: null, - descriptor: response.propertyDescriptor - }; - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewProperty = + this.propertyTableHelperService.createNewProperty(registryClientId, this.registryClientService); editDialogReference.componentInstance.goToService = (serviceId: string) => { const commands: string[] = ['/settings', 'management-controller-services', serviceId]; @@ -242,77 +202,12 @@ export class RegistryClientsEffects { } }; - editDialogReference.componentInstance.createNewService = ( - request: InlineServiceCreationRequest - ): Observable => { - const descriptor: PropertyDescriptor = request.descriptor; - - // fetch all services that implement the requested service api - return this.extensionTypesService - .getImplementingControllerServiceTypes( - // @ts-ignore - descriptor.identifiesControllerService, - descriptor.identifiesControllerServiceBundle - ) - .pipe( - take(1), - switchMap((implementingTypesResponse) => { - // show the create controller service dialog with the types that implemented the interface - const createServiceDialogReference = this.dialog.open(CreateControllerService, { - data: { - controllerServiceTypes: implementingTypesResponse.controllerServiceTypes - }, - panelClass: 'medium-dialog' - }); - - return createServiceDialogReference.componentInstance.createControllerService.pipe( - take(1), - switchMap((controllerServiceType) => { - // typically this sequence would be implemented with ngrx actions, however we are - // currently in an edit session and we need to return both the value (new service id) - // and updated property descriptor so the table renders correctly - return this.managementControllerServiceService - .createControllerService({ - revision: { - clientId: this.client.getClientId(), - version: 0 - }, - controllerServiceType: controllerServiceType.type, - controllerServiceBundle: controllerServiceType.bundle - }) - .pipe( - take(1), - switchMap((createReponse) => { - // fetch an updated property descriptor - return this.registryClientService - .getPropertyDescriptor( - registryClientId, - descriptor.name, - false - ) - .pipe( - take(1), - map((descriptorResponse) => { - createServiceDialogReference.close(); - - return { - value: createReponse.id, - descriptor: - descriptorResponse.propertyDescriptor - }; - }) - ); - }), - catchError((error) => { - // TODO - show error - return NEVER; - }) - ); - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewService = + this.propertyTableHelperService.createNewService( + registryClientId, + this.managementControllerServiceService, + this.registryClientService + ); editDialogReference.componentInstance.editRegistryClient .pipe(takeUntil(editDialogReference.afterClosed())) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts index 05528bf004..b668f3da78 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/state/reporting-tasks/reporting-tasks.effects.ts @@ -44,6 +44,7 @@ import { ExtensionTypesService } from '../../../../service/extension-types.servi import { CreateControllerService } from '../../../../ui/common/controller-service/create-controller-service/create-controller-service.component'; import { ManagementControllerServiceService } from '../../service/management-controller-service.service'; import { Client } from '../../../../service/client.service'; +import { PropertyTableHelperService } from '../../../../service/property-table-helper.service'; @Injectable() export class ReportingTasksEffects { @@ -55,7 +56,8 @@ export class ReportingTasksEffects { private managementControllerServiceService: ManagementControllerServiceService, private extensionTypesService: ExtensionTypesService, private dialog: MatDialog, - private router: Router + private router: Router, + private propertyTableHelperService: PropertyTableHelperService ) {} loadReportingTasks$ = createEffect(() => @@ -225,36 +227,8 @@ export class ReportingTasksEffects { editDialogReference.componentInstance.saving$ = this.store.select(selectSaving); - editDialogReference.componentInstance.createNewProperty = ( - existingProperties: string[], - allowsSensitive: boolean - ): Observable => { - const dialogRequest: NewPropertyDialogRequest = { existingProperties, allowsSensitive }; - const newPropertyDialogReference = this.dialog.open(NewPropertyDialog, { - data: dialogRequest, - panelClass: 'small-dialog' - }); - - return newPropertyDialogReference.componentInstance.newProperty.pipe( - take(1), - switchMap((dialogResponse: NewPropertyDialogResponse) => { - return this.reportingTaskService - .getPropertyDescriptor(request.id, dialogResponse.name, dialogResponse.sensitive) - .pipe( - take(1), - map((response) => { - newPropertyDialogReference.close(); - - return { - property: dialogResponse.name, - value: null, - descriptor: response.propertyDescriptor - }; - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewProperty = + this.propertyTableHelperService.createNewProperty(request.id, this.reportingTaskService); const goTo = (commands: string[], destination: string): void => { if (editDialogReference.componentInstance.editReportingTaskForm.dirty) { @@ -285,73 +259,12 @@ export class ReportingTasksEffects { goTo(commands, 'Controller Service'); }; - editDialogReference.componentInstance.createNewService = ( - request: InlineServiceCreationRequest - ): Observable => { - const descriptor: PropertyDescriptor = request.descriptor; - - // fetch all services that implement the requested service api - return this.extensionTypesService - .getImplementingControllerServiceTypes( - // @ts-ignore - descriptor.identifiesControllerService, - descriptor.identifiesControllerServiceBundle - ) - .pipe( - take(1), - switchMap((implementingTypesResponse) => { - // show the create controller service dialog with the types that implemented the interface - const createServiceDialogReference = this.dialog.open(CreateControllerService, { - data: { - controllerServiceTypes: implementingTypesResponse.controllerServiceTypes - }, - panelClass: 'medium-dialog' - }); - - return createServiceDialogReference.componentInstance.createControllerService.pipe( - take(1), - switchMap((controllerServiceType) => { - // typically this sequence would be implemented with ngrx actions, however we are - // currently in an edit session and we need to return both the value (new service id) - // and updated property descriptor so the table renders correctly - return this.managementControllerServiceService - .createControllerService({ - revision: { - clientId: this.client.getClientId(), - version: 0 - }, - controllerServiceType: controllerServiceType.type, - controllerServiceBundle: controllerServiceType.bundle - }) - .pipe( - take(1), - switchMap((createResponse) => { - // fetch an updated property descriptor - return this.reportingTaskService - .getPropertyDescriptor(taskId, descriptor.name, false) - .pipe( - take(1), - map((descriptorResponse) => { - createServiceDialogReference.close(); - - return { - value: createResponse.id, - descriptor: - descriptorResponse.propertyDescriptor - }; - }) - ); - }), - catchError((error) => { - // TODO - show error - return NEVER; - }) - ); - }) - ); - }) - ); - }; + editDialogReference.componentInstance.createNewService = + this.propertyTableHelperService.createNewService( + request.id, + this.managementControllerServiceService, + this.reportingTaskService + ); editDialogReference.componentInstance.editReportingTask .pipe(takeUntil(editDialogReference.afterClosed())) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.html new file mode 100644 index 0000000000..a1a278685e --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.html @@ -0,0 +1,22 @@ + + + diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.scss new file mode 100644 index 0000000000..b33f7cac34 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.scss @@ -0,0 +1,16 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.spec.ts new file mode 100644 index 0000000000..25517a106f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.spec.ts @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateParameterProvider } from './create-parameter-provider.component'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { provideMockStore } from '@ngrx/store/testing'; +import { initialParameterProvidersState } from '../../../state/parameter-providers/parameter-providers.reducer'; +import { + CreateParameterProviderDialogRequest, + CreateParameterProviderRequest +} from '../../../state/parameter-providers'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +describe('CreateParameterProvider', () => { + let component: CreateParameterProvider; + let fixture: ComponentFixture; + const data: CreateParameterProviderDialogRequest = { + parameterProviderTypes: [ + { + type: 'org.apache.nifi.parameter.aws.AwsSecretsManagerParameterProvider', + bundle: { + group: 'org.apache.nifi', + artifact: 'nifi-aws-nar', + version: '2.0.0-SNAPSHOT' + }, + description: + 'Fetches parameters from AWS SecretsManager. Each secret becomes a Parameter group, which can map to a Parameter Context, with key/value pairs in the secret mapping to Parameters in the group.', + restricted: false, + tags: ['secretsmanager', 'manager', 'aws', 'secrets'] + }, + { + type: 'org.apache.nifi.parameter.azure.AzureKeyVaultSecretsParameterProvider', + bundle: { + group: 'org.apache.nifi', + artifact: 'nifi-azure-nar', + version: '2.0.0-SNAPSHOT' + }, + description: + "Fetches parameters from Azure Key Vault Secrets. Each secret becomes a Parameter, which map to a Parameter Group byadding a secret tag named 'group-name'.", + restricted: false, + tags: ['keyvault', 'secrets', 'key', 'vault', 'azure'] + } + ] + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [CreateParameterProvider, NoopAnimationsModule], + providers: [ + { + provide: MAT_DIALOG_DATA, + useValue: data + }, + provideMockStore({ initialState: initialParameterProvidersState }) + ] + }); + fixture = TestBed.createComponent(CreateParameterProvider); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.ts new file mode 100644 index 0000000000..a23e226a8a --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/create-parameter-provider/create-parameter-provider.component.ts @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, EventEmitter, Inject, Input, Output } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { Observable } from 'rxjs'; +import { DocumentedType } from '../../../../../state/shared'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { CreateParameterProviderDialogRequest } from '../../../state/parameter-providers'; +import { ExtensionCreation } from '../../../../../ui/common/extension-creation/extension-creation.component'; + +@Component({ + selector: 'create-parameter-provider', + standalone: true, + imports: [CommonModule, ExtensionCreation], + templateUrl: './create-parameter-provider.component.html', + styleUrls: ['./create-parameter-provider.component.scss'] +}) +export class CreateParameterProvider { + @Input() saving$!: Observable; + @Output() createParameterProvider: EventEmitter = new EventEmitter(); + + parameterProviderTypes: DocumentedType[]; + + constructor(@Inject(MAT_DIALOG_DATA) private dialogRequest: CreateParameterProviderDialogRequest) { + this.parameterProviderTypes = dialogRequest.parameterProviderTypes; + } + + parameterProviderTypeSelected(parameterProviderType: DocumentedType) { + this.createParameterProvider.next(parameterProviderType); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.html new file mode 100644 index 0000000000..a2eaf019a2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.html @@ -0,0 +1,91 @@ + + +

Edit Parameter Provider

+
+ + + +
+
+
+ + Name + + +
+
+
Id
+
{{ request.parameterProvider.id }}
+
+
+
Type
+
{{ formatType(request.parameterProvider) }}
+
+
+
Bundle
+
{{ formatBundle(request.parameterProvider) }}
+
+
+
+
Referencing Components
+
+ +
+
+
+
+ + +
+ +
+
+ + +
+ + Comments + + +
+
+
+
+ + + + + +
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.scss new file mode 100644 index 0000000000..717bc5565d --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.scss @@ -0,0 +1,36 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@use '@angular/material' as mat; + +.parameter-provider-edit-form { + @include mat.button-density(-1); + + .mdc-dialog__content { + padding: 0 16px; + font-size: 14px; + + .tab-content { + height: 475px; + overflow-y: auto; + } + } + + .mat-mdc-form-field { + width: 100%; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.spec.ts new file mode 100644 index 0000000000..dccbe3d0b2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.spec.ts @@ -0,0 +1,170 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditParameterProvider } from './edit-parameter-provider.component'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { EditParameterProviderRequest } from '../../../state/parameter-providers'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +describe('EditParameterProvider', () => { + let component: EditParameterProvider; + let fixture: ComponentFixture; + const data: EditParameterProviderRequest = { + id: 'id', + parameterProvider: { + revision: { + clientId: '36ba1cc1-018d-1000-bc2c-787bc552d63d', + version: 6 + }, + id: '369487d7-018d-1000-817a-1d8d9a8f4a91', + uri: 'https://localhost:8443/nifi-api/parameter-providers/369487d7-018d-1000-817a-1d8d9a8f4a91', + permissions: { + canRead: true, + canWrite: true + }, + bulletins: [], + component: { + id: '369487d7-018d-1000-817a-1d8d9a8f4a91', + name: 'Group 1 - FileParameterProvider', + type: 'org.apache.nifi.parameter.FileParameterProvider', + bundle: { + group: 'org.apache.nifi', + artifact: 'nifi-standard-nar', + version: '2.0.0-SNAPSHOT' + }, + comments: '', + persistsState: false, + restricted: true, + deprecated: false, + multipleVersionsAvailable: false, + properties: { + 'parameter-group-directories': '/Users/rfellows/tmp/parameterProviders/group1', + 'parameter-value-byte-limit': '256 B', + 'parameter-value-encoding': 'plaintext' + }, + descriptors: { + 'parameter-group-directories': { + name: 'parameter-group-directories', + displayName: 'Parameter Group Directories', + description: + 'A comma-separated list of directory absolute paths that will map to named parameter groups. Each directory that contains files will map to a parameter group, named after the innermost directory in the path. Files inside the directory will map to parameter names, whose values are the content of each respective file.', + required: true, + sensitive: false, + dynamic: false, + supportsEl: false, + expressionLanguageScope: 'Not Supported', + dependencies: [] + }, + 'parameter-value-byte-limit': { + name: 'parameter-value-byte-limit', + displayName: 'Parameter Value Byte Limit', + description: + 'The maximum byte size of a parameter value. Since parameter values are pulled from the contents of files, this is a safeguard that can prevent memory issues if large files are included.', + defaultValue: '256 B', + required: true, + sensitive: false, + dynamic: false, + supportsEl: false, + expressionLanguageScope: 'Not Supported', + dependencies: [] + }, + 'parameter-value-encoding': { + name: 'parameter-value-encoding', + displayName: 'Parameter Value Encoding', + description: 'Indicates how parameter values are encoded inside Parameter files.', + defaultValue: 'base64', + allowableValues: [ + { + allowableValue: { + displayName: 'Base64', + value: 'base64', + description: + 'File content is Base64-encoded, and will be decoded before providing the value as a Parameter.' + }, + canRead: true + }, + { + allowableValue: { + displayName: 'Plain text', + value: 'plaintext', + description: + 'File content is not encoded, and will be provided directly as a Parameter value.' + }, + canRead: true + } + ], + required: true, + sensitive: false, + dynamic: false, + supportsEl: false, + expressionLanguageScope: 'Not Supported', + dependencies: [] + } + }, + parameterGroupConfigurations: [ + { + groupName: 'group1', + parameterContextName: 'group1', + parameterSensitivities: { + bytes: 'NON_SENSITIVE', + password: 'SENSITIVE', + username: 'NON_SENSITIVE' + }, + synchronized: true + } + ], + referencingParameterContexts: [ + { + id: '3716e18d-018d-1000-f203-4f6d571d572e', + permissions: { + canRead: true, + canWrite: true + }, + bulletins: [], + component: { + id: '3716e18d-018d-1000-f203-4f6d571d572e', + name: 'group1' + } + } + ], + validationStatus: 'VALID', + extensionMissing: false + } + } + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [EditParameterProvider, NoopAnimationsModule], + providers: [ + { + provide: MAT_DIALOG_DATA, + useValue: data + } + ] + }); + fixture = TestBed.createComponent(EditParameterProvider); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.ts new file mode 100644 index 0000000000..2032ab2868 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component.ts @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, EventEmitter, Inject, Input, Output } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatButtonModule } from '@angular/material/button'; +import { NifiSpinnerDirective } from '../../../../../ui/common/spinner/nifi-spinner.directive'; +import { Observable } from 'rxjs'; +import { + ControllerServiceReferencingComponent, + InlineServiceCreationRequest, + InlineServiceCreationResponse, + ParameterContextReferenceEntity, + Property +} from '../../../../../state/shared'; +import { + EditParameterProviderRequest, + ParameterProviderEntity, + UpdateParameterProviderRequest +} from '../../../state/parameter-providers'; +import { AbstractControl, FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; +import { Client } from '../../../../../service/client.service'; +import { NiFiCommon } from '../../../../../service/nifi-common.service'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { ControllerServiceReferences } from '../../../../../ui/common/controller-service/controller-service-references/controller-service-references.component'; +import { ParameterProviderReferences } from '../parameter-context-references/parameter-provider-references.component'; +import { PropertyTable } from '../../../../../ui/common/property-table/property-table.component'; + +@Component({ + selector: 'edit-parameter-provider', + standalone: true, + imports: [ + CommonModule, + MatDialogModule, + MatTabsModule, + MatButtonModule, + NifiSpinnerDirective, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + ControllerServiceReferences, + ParameterProviderReferences, + PropertyTable + ], + templateUrl: './edit-parameter-provider.component.html', + styleUrls: ['./edit-parameter-provider.component.scss'] +}) +export class EditParameterProvider { + @Input() createNewProperty!: (existingProperties: string[], allowsSensitive: boolean) => Observable; + @Input() createNewService!: (request: InlineServiceCreationRequest) => Observable; + @Input() goToService!: (serviceId: string) => void; + @Input() goToReferencingParameterContext!: (parameterContextId: string) => void; + @Input() saving$!: Observable; + + @Output() editParameterProvider: EventEmitter = + new EventEmitter(); + + editParameterProviderForm: FormGroup; + + constructor( + @Inject(MAT_DIALOG_DATA) public request: EditParameterProviderRequest, + private formBuilder: FormBuilder, + private client: Client, + private nifiCommon: NiFiCommon + ) { + const providerProperties = request.parameterProvider.component.properties; + const properties: Property[] = Object.entries(providerProperties).map((entry: any) => { + const [property, value] = entry; + return { + property, + value, + descriptor: request.parameterProvider.component.descriptors[property] + }; + }); + + // build the form + this.editParameterProviderForm = this.formBuilder.group({ + name: new FormControl(request.parameterProvider.component.name, Validators.required), + properties: new FormControl(properties), + comments: new FormControl(request.parameterProvider.component.comments) + }); + } + + formatType(entity: ParameterProviderEntity): string { + return this.nifiCommon.formatType(entity.component); + } + + formatBundle(entity: ParameterProviderEntity): string { + return this.nifiCommon.formatBundle(entity.component.bundle); + } + + submitForm(postUpdateNavigation?: string[]) { + const payload: any = { + revision: this.client.getRevision(this.request.parameterProvider), + component: { + id: this.request.parameterProvider.id, + name: this.editParameterProviderForm.get('name')?.value, + comments: this.editParameterProviderForm.get('comments')?.value + } + }; + + const propertyControl: AbstractControl | null = this.editParameterProviderForm.get('properties'); + if (propertyControl && propertyControl.dirty) { + const properties: Property[] = propertyControl.value; + const values: { [key: string]: string | null } = {}; + properties.forEach((property) => (values[property.property] = property.value)); + payload.component.properties = values; + payload.component.sensitiveDynamicPropertyNames = properties + .filter((property) => property.descriptor.dynamic && property.descriptor.sensitive) + .map((property) => property.descriptor.name); + } + + this.editParameterProvider.next({ + payload, + postUpdateNavigation + }); + } + + navigateToParameterContext(parameterContextReference: ParameterContextReferenceEntity) { + if (parameterContextReference.component?.id) { + this.goToReferencingParameterContext(parameterContextReference.component?.id); + } + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-provider-references.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-provider-references.component.spec.ts new file mode 100644 index 0000000000..f7a7d2ec23 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-provider-references.component.spec.ts @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ParameterProviderReferences } from './parameter-provider-references.component'; + +describe('ParameterProviderReferences', () => { + let component: ParameterProviderReferences; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ParameterProviderReferences] + }); + fixture = TestBed.createComponent(ParameterProviderReferences); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-provider-references.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-provider-references.component.ts new file mode 100644 index 0000000000..652aaac777 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-provider-references.component.ts @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ParameterContextReferenceEntity } from '../../../../../state/shared'; +import { NiFiCommon } from '../../../../../service/nifi-common.service'; +import { RouterLink } from '@angular/router'; + +@Component({ + selector: 'parameter-provider-references', + standalone: true, + imports: [CommonModule, RouterLink], + templateUrl: './parameter-providers-references.component.html', + styleUrls: ['./parameter-providers-references.component.scss'] +}) +export class ParameterProviderReferences { + @Input() parameterProviderReferences!: ParameterContextReferenceEntity[]; + @Output() goToParameterContext: EventEmitter = + new EventEmitter(); + + constructor(private nifiCommon: NiFiCommon) {} + + getUnauthorized(references: ParameterContextReferenceEntity[]) { + return references.filter((p) => !p.permissions.canRead); + } + + getAuthorized(references: ParameterContextReferenceEntity[]) { + return references.filter((p) => p.permissions.canRead); + } + + goToParameterContextClicked(event: MouseEvent, parameterContextReference: ParameterContextReferenceEntity) { + event.stopPropagation(); + this.goToParameterContext.next(parameterContextReference); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-providers-references.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-providers-references.component.html new file mode 100644 index 0000000000..84ec9b4444 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-providers-references.component.html @@ -0,0 +1,76 @@ + + +
+
+ No referencing components +
+ + +
    + + + +
+
+ + + +
  • +

    + {{ label }} ({{ references.length }}) +

    + +
  • +
    +
    + + + +
  • +

    + {{ label }} ({{ references.length }}) +

    +
    +
    +
    {{ reference.id }}
    +
    +
    +
  • +
    +
    +
    diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-providers-references.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-providers-references.component.scss new file mode 100644 index 0000000000..c6abda8ba2 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-context-references/parameter-providers-references.component.scss @@ -0,0 +1,26 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.parameter-provider-references { + ul.nested { + padding-inline-start: 20px; + } + + .references { + margin-left: 20px; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.html new file mode 100644 index 0000000000..7c3eb1e925 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.html @@ -0,0 +1,121 @@ + + + +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + +
    + + +
    + + +
    +
    +
    +
    +
    +
    Name
    +
    +
    {{ formatName(item) }}
    +
    +
    Type
    +
    + {{ formatType(item) }} + +
    Bundle
    +
    + {{ formatBundle(item) }} + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.scss b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.scss new file mode 100644 index 0000000000..e44f2b386a --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.scss @@ -0,0 +1,22 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.parameter-providers-table { + .listing-table { + // overrides to the listing-table styles go here + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.spec.ts new file mode 100644 index 0000000000..5f6a0e3767 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.spec.ts @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ParameterProvidersTable } from './parameter-providers-table.component'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; + +describe('ParameterProvidersTable', () => { + let component: ParameterProvidersTable; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ParameterProvidersTable, NoopAnimationsModule] + }); + fixture = TestBed.createComponent(ParameterProvidersTable); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.ts new file mode 100644 index 0000000000..2916239c6e --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers-table/parameter-providers-table.component.ts @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { MatSortModule, Sort, SortDirection } from '@angular/material/sort'; +import { MatTableDataSource, MatTableModule } from '@angular/material/table'; +import { ParameterProviderEntity } from '../../../state/parameter-providers'; +import { NiFiCommon } from '../../../../../service/nifi-common.service'; +import { CurrentUser } from '../../../../../state/current-user'; +import { FlowConfiguration } from '../../../../../state/flow-configuration'; +import { MatPaginatorModule } from '@angular/material/paginator'; +import { SummaryTableFilterModule } from '../../../../summary/ui/common/summary-table-filter/summary-table-filter.module'; +import { PortStatusSnapshotEntity } from '../../../../summary/state/summary-listing'; +import { ValidationErrorsTip } from '../../../../../ui/common/tooltips/validation-errors-tip/validation-errors-tip.component'; +import { NifiTooltipDirective } from '../../../../../ui/common/tooltips/nifi-tooltip.directive'; +import { ControllerServiceEntity, ValidationErrorsTipInput } from '../../../../../state/shared'; +import { RouterLink } from '@angular/router'; + +export type SupportedColumns = 'name' | 'type' | 'bundle'; + +@Component({ + selector: 'parameter-providers-table', + standalone: true, + imports: [ + CommonModule, + MatPaginatorModule, + MatSortModule, + MatTableModule, + SummaryTableFilterModule, + NifiTooltipDirective, + RouterLink + ], + templateUrl: './parameter-providers-table.component.html', + styleUrls: ['./parameter-providers-table.component.scss', '../../../../../../assets/styles/listing-table.scss'] +}) +export class ParameterProvidersTable { + @Input() initialSortColumn: SupportedColumns = 'name'; + @Input() initialSortDirection: SortDirection = 'asc'; + + displayedColumns: string[] = ['moreDetails', 'name', 'type', 'bundle', 'actions']; + dataSource: MatTableDataSource = new MatTableDataSource(); + activeSort: Sort = { + active: this.initialSortColumn, + direction: this.initialSortDirection + }; + + constructor(private nifiCommon: NiFiCommon) {} + + @Input() selectedParameterProviderId!: string; + @Input() currentUser!: CurrentUser; + @Input() flowConfiguration!: FlowConfiguration; + + @Input() set parameterProviders(parameterProviders: ParameterProviderEntity[]) { + if (parameterProviders) { + this.dataSource.data = this.sortEntities(parameterProviders, this.activeSort); + } + } + + @Output() selectParameterProvider: EventEmitter = + new EventEmitter(); + @Output() configureParameterProvider: EventEmitter = + new EventEmitter(); + @Output() deleteParameterProvider: EventEmitter = + new EventEmitter(); + @Output() fetchParameterProvider: EventEmitter = + new EventEmitter(); + @Output() manageAccessPolicies: EventEmitter = new EventEmitter(); + + protected readonly ValidationErrorsTip = ValidationErrorsTip; + + canRead(entity: ParameterProviderEntity): boolean { + return entity.permissions.canRead; + } + + canWrite(entity: ParameterProviderEntity): boolean { + return entity.permissions.canWrite; + } + + canManageAccessPolicies(): boolean { + return this.flowConfiguration.supportsManagedAuthorizer && this.currentUser.tenantsPermissions.canRead; + } + + isSelected(parameterProvider: ParameterProviderEntity): boolean { + if (this.selectedParameterProviderId) { + return parameterProvider.id === this.selectedParameterProviderId; + } + return false; + } + + formatName(entity: ParameterProviderEntity): string { + return this.canRead(entity) ? entity.component.name : entity.id; + } + + formatType(entity: ParameterProviderEntity): string { + return this.canRead(entity) ? this.nifiCommon.formatType(entity.component) : ''; + } + + formatBundle(entity: ParameterProviderEntity): string { + return this.canRead(entity) ? this.nifiCommon.formatBundle(entity.component.bundle) : ''; + } + + hasErrors(entity: ParameterProviderEntity): boolean { + return this.canRead(entity) && !this.nifiCommon.isEmpty(entity.component.validationErrors); + } + + getValidationErrorsTipData(entity: ParameterProviderEntity): ValidationErrorsTipInput | null { + return { + isValidating: entity.component.validationStatus === 'VALIDATING', + validationErrors: entity.component?.validationErrors || [] + }; + } + + sortData(sort: Sort) { + this.activeSort = sort; + this.dataSource.data = this.sortEntities(this.dataSource.data, sort); + } + + private sortEntities(data: ParameterProviderEntity[], sort: Sort): ParameterProviderEntity[] { + if (!data) { + return []; + } + return data.slice().sort((a, b) => { + const isAsc: boolean = sort.direction === 'asc'; + let retVal = 0; + switch (sort.active) { + case 'name': + retVal = this.nifiCommon.compareString(this.formatName(a), this.formatName(b)); + break; + case 'type': + retVal = this.nifiCommon.compareString(this.formatType(a), this.formatType(b)); + break; + case 'bundle': + retVal = this.nifiCommon.compareString(this.formatBundle(a), this.formatBundle(b)); + break; + default: + return 0; + } + + return retVal * (isAsc ? 1 : -1); + }); + } + + configureClicked(entity: ParameterProviderEntity, event: MouseEvent) { + event.stopPropagation(); + this.configureParameterProvider.next(entity); + } + + fetchClicked(entity: ParameterProviderEntity, event: MouseEvent) { + event.stopPropagation(); + this.fetchParameterProvider.next(entity); + } + + deleteClicked(entity: ParameterProviderEntity, event: MouseEvent) { + event.stopPropagation(); + this.deleteParameterProvider.next(entity); + } + + getPolicyLink(entity: ParameterProviderEntity): string[] { + return ['/access-policies', 'read', 'component', 'parameter-providers', entity.id]; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.html b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.html index 1e7c10a0b4..d0b715d847 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.html +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.html @@ -15,4 +15,39 @@ ~ limitations under the License. --> -

    parameter-providers works!

    + +
    + +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    Last updated:
    +
    {{ parameterProvidersState.loadedTimestamp }}
    +
    +
    +
    +
    +
    diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.spec.ts index 842641ea8b..66357cb49c 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.spec.ts @@ -18,6 +18,8 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ParameterProviders } from './parameter-providers.component'; +import { provideMockStore } from '@ngrx/store/testing'; +import { initialParameterProvidersState } from '../../state/parameter-providers/parameter-providers.reducer'; describe('ParameterProviders', () => { let component: ParameterProviders; @@ -25,7 +27,12 @@ describe('ParameterProviders', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [ParameterProviders] + declarations: [ParameterProviders], + providers: [ + provideMockStore({ + initialState: initialParameterProvidersState + }) + ] }); fixture = TestBed.createComponent(ParameterProviders); component = fixture.componentInstance; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.ts index 1149a35b5f..aab8243728 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.component.ts @@ -15,11 +15,105 @@ * limitations under the License. */ -import { Component } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { Store } from '@ngrx/store'; +import { NiFiState } from '../../../../state'; +import { ParameterProviderEntity, ParameterProvidersState } from '../../state/parameter-providers'; +import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors'; +import { + selectParameterProvider, + selectParameterProviderIdFromRoute, + selectParameterProvidersState, + selectSingleEditedParameterProvider +} from '../../state/parameter-providers/parameter-providers.selectors'; +import { selectFlowConfiguration } from '../../../../state/flow-configuration/flow-configuration.selectors'; +import { loadFlowConfiguration } from '../../../../state/flow-configuration/flow-configuration.actions'; +import * as ParameterProviderActions from '../../state/parameter-providers/parameter-providers.actions'; +import { initialParameterProvidersState } from '../../state/parameter-providers/parameter-providers.reducer'; +import { filter, switchMap, take } from 'rxjs'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { isDefinedAndNotNull } from '../../../../state/shared'; @Component({ selector: 'parameter-providers', templateUrl: './parameter-providers.component.html', styleUrls: ['./parameter-providers.component.scss'] }) -export class ParameterProviders {} +export class ParameterProviders implements OnInit, OnDestroy { + currentUser$ = this.store.select(selectCurrentUser); + parameterProvidersState$ = this.store.select(selectParameterProvidersState); + selectedParameterProviderId$ = this.store.select(selectParameterProviderIdFromRoute); + flowConfiguration$ = this.store.select(selectFlowConfiguration); + + constructor(private store: Store) { + this.store + .select(selectSingleEditedParameterProvider) + .pipe( + isDefinedAndNotNull(), + switchMap((id: string) => + this.store.select(selectParameterProvider(id)).pipe(isDefinedAndNotNull(), take(1)) + ), + takeUntilDestroyed() + ) + .subscribe((entity) => { + this.store.dispatch( + ParameterProviderActions.openConfigureParameterProviderDialog({ + request: { + id: entity.id, + parameterProvider: entity + } + }) + ); + }); + } + + ngOnInit(): void { + this.store.dispatch(loadFlowConfiguration()); + this.store.dispatch(ParameterProviderActions.loadParameterProviders()); + } + + ngOnDestroy(): void { + this.store.dispatch(ParameterProviderActions.resetParameterProvidersState()); + } + + isInitialLoading(state: ParameterProvidersState): boolean { + // using the current timestamp to detect the initial load event + return state.loadedTimestamp == initialParameterProvidersState.loadedTimestamp; + } + + refreshParameterProvidersListing(): void { + this.store.dispatch(ParameterProviderActions.loadParameterProviders()); + } + + openNewParameterProviderDialog() { + this.store.dispatch(ParameterProviderActions.openNewParameterProviderDialog()); + } + + openConfigureParameterProviderDialog(parameterProvider: ParameterProviderEntity) { + this.store.dispatch( + ParameterProviderActions.navigateToEditParameterProvider({ + id: parameterProvider.component.id + }) + ); + } + + selectParameterProvider(parameterProvider: ParameterProviderEntity) { + this.store.dispatch( + ParameterProviderActions.selectParameterProvider({ + request: { + id: parameterProvider.id + } + }) + ); + } + + deleteParameterProvider(parameterProvider: ParameterProviderEntity) { + this.store.dispatch( + ParameterProviderActions.promptParameterProviderDeletion({ + request: { + parameterProvider + } + }) + ); + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.module.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.module.ts index 9eff52810f..6ea65f94f6 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.module.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/parameter-providers/parameter-providers.module.ts @@ -18,10 +18,13 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ParameterProviders } from './parameter-providers.component'; +import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; +import { ParameterContextListingModule } from '../../../parameter-contexts/ui/parameter-context-listing/parameter-context-listing.module'; +import { ParameterProvidersTable } from './parameter-providers-table/parameter-providers-table.component'; @NgModule({ declarations: [ParameterProviders], exports: [ParameterProviders], - imports: [CommonModule] + imports: [CommonModule, NgxSkeletonLoaderModule, ParameterProvidersTable] }) export class ParameterProvidersModule {} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts index a09304f2ee..b63c92162d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/settings/ui/reporting-tasks/reporting-task-table/reporting-task-table.component.ts @@ -35,12 +35,13 @@ import { CurrentUser } from '../../../../../state/current-user'; export class ReportingTaskTable { @Input() initialSortColumn: 'name' | 'type' | 'bundle' | 'state' = 'name'; @Input() initialSortDirection: 'asc' | 'desc' = 'asc'; + activeSort: Sort = { + active: this.initialSortColumn, + direction: this.initialSortDirection + }; @Input() set reportingTasks(reportingTaskEntities: ReportingTaskEntity[]) { - this.dataSource.data = this.sortEntities(reportingTaskEntities, { - active: this.initialSortColumn, - direction: this.initialSortDirection - }); + this.dataSource.data = this.sortEntities(reportingTaskEntities, this.activeSort); } @Input() selectedReportingTaskId!: string; @@ -258,6 +259,7 @@ export class ReportingTaskTable { } sortData(sort: Sort) { + this.activeSort = sort; this.dataSource.data = this.sortEntities(this.dataSource.data, sort); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/users/service/users.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/users/service/users.service.ts index 2b1f18e95f..75d8dfa953 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/users/service/users.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/pages/users/service/users.service.ts @@ -32,19 +32,6 @@ import { export class UsersService { private static readonly API: string = '../nifi-api'; - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - constructor( private httpClient: HttpClient, private client: Client, @@ -83,7 +70,7 @@ export class UsersService { ...request.userPayload } }; - return this.httpClient.put(this.stripProtocol(request.uri), payload); + return this.httpClient.put(this.nifiCommon.stripProtocol(request.uri), payload); } updateUserGroup(request: UpdateUserGroupRequest): Observable { @@ -94,16 +81,16 @@ export class UsersService { ...request.userGroupPayload } }; - return this.httpClient.put(this.stripProtocol(request.uri), payload); + return this.httpClient.put(this.nifiCommon.stripProtocol(request.uri), payload); } deleteUser(user: UserEntity): Observable { const revision: any = this.client.getRevision(user); - return this.httpClient.delete(this.stripProtocol(user.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(user.uri), { params: revision }); } deleteUserGroup(userGroup: UserGroupEntity): Observable { const revision: any = this.client.getRevision(userGroup); - return this.httpClient.delete(this.stripProtocol(userGroup.uri), { params: revision }); + return this.httpClient.delete(this.nifiCommon.stripProtocol(userGroup.uri), { params: revision }); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/component-state.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/component-state.service.ts index f87d93b7b4..1fef85eb30 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/component-state.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/component-state.service.ts @@ -28,24 +28,11 @@ export class ComponentStateService { private nifiCommon: NiFiCommon ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - getComponentState(request: LoadComponentStateRequest): Observable { - return this.httpClient.get(`${this.stripProtocol(request.componentUri)}/state`); + return this.httpClient.get(`${this.nifiCommon.stripProtocol(request.componentUri)}/state`); } clearComponentState(request: ClearComponentStateRequest): Observable { - return this.httpClient.post(`${this.stripProtocol(request.componentUri)}/state/clear-requests`, {}); + return this.httpClient.post(`${this.nifiCommon.stripProtocol(request.componentUri)}/state/clear-requests`, {}); } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/controller-service-state.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/controller-service-state.service.ts index 679ad50685..7d446c5aa7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/controller-service-state.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/controller-service-state.service.ts @@ -36,25 +36,12 @@ export class ControllerServiceStateService { private client: Client ) {} - /** - * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling - * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. - * - * https://stackoverflow.com/a/59586462 - * - * @param url - * @private - */ - private stripProtocol(url: string): string { - return this.nifiCommon.substringAfterFirst(url, ':'); - } - getControllerService(id: string): Observable { return this.httpClient.get(`${ControllerServiceStateService.API}/controller-services/${id}`); } setEnable(controllerService: ControllerServiceEntity, enabled: boolean): Observable { - return this.httpClient.put(`${this.stripProtocol(controllerService.uri)}/run-status`, { + return this.httpClient.put(`${this.nifiCommon.stripProtocol(controllerService.uri)}/run-status`, { revision: this.client.getRevision(controllerService), state: enabled ? 'ENABLED' : 'DISABLED', uiOnly: true @@ -69,7 +56,7 @@ export class ControllerServiceStateService { true ); - return this.httpClient.put(`${this.stripProtocol(controllerService.uri)}/references`, { + return this.httpClient.put(`${this.nifiCommon.stripProtocol(controllerService.uri)}/references`, { id: controllerService.id, state: enabled ? 'ENABLED' : 'DISABLED', referencingComponentRevisions: referencingComponentRevisions, @@ -89,7 +76,7 @@ export class ControllerServiceStateService { false ); - return this.httpClient.put(`${this.stripProtocol(controllerService.uri)}/references`, { + return this.httpClient.put(`${this.nifiCommon.stripProtocol(controllerService.uri)}/references`, { id: controllerService.id, state: running ? 'RUNNING' : 'STOPPED', referencingComponentRevisions: referencingComponentRevisions, diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/nifi-common.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/nifi-common.service.ts index 556cc95ba7..85ec206b4f 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/nifi-common.service.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/nifi-common.service.ts @@ -511,4 +511,17 @@ export class NiFiCommon { public getAllPolicyTypeListing(): SelectOption[] { return this.policyTypeListing; } + + /** + * The NiFi model contain the url for each component. That URL is an absolute URL. Angular CSRF handling + * does not work on absolute URLs, so we need to strip off the proto for the request header to be added. + * + * https://stackoverflow.com/a/59586462 + * + * @param url + * @private + */ + public stripProtocol(url: string): string { + return this.substringAfterFirst(url, ':'); + } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/property-table-helper.service.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/property-table-helper.service.ts new file mode 100644 index 0000000000..c359980e6f --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/service/property-table-helper.service.ts @@ -0,0 +1,177 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Injectable } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { catchError, map, NEVER, Observable, switchMap, take } from 'rxjs'; +import { + ControllerServiceCreator, + ControllerServiceEntity, + CreateControllerServiceRequest, + InlineServiceCreationRequest, + InlineServiceCreationResponse, + NewPropertyDialogRequest, + NewPropertyDialogResponse, + Property, + PropertyDescriptor, + PropertyDescriptorRetriever +} from '../state/shared'; +import { NewPropertyDialog } from '../ui/common/new-property-dialog/new-property-dialog.component'; +import { CreateControllerService } from '../ui/common/controller-service/create-controller-service/create-controller-service.component'; +import { ManagementControllerServiceService } from '../pages/settings/service/management-controller-service.service'; +import { ExtensionTypesService } from './extension-types.service'; +import { Client } from './client.service'; + +@Injectable({ + providedIn: 'root' +}) +export class PropertyTableHelperService { + constructor( + private dialog: MatDialog, + private extensionTypesService: ExtensionTypesService, + private client: Client + ) {} + + /** + * Returns a function that can be used to pass into a PropertyTable to support creating a new property + * @param id id of the component to create the property for + * @param propertyDescriptorService the service to call to get property descriptors + */ + createNewProperty( + id: string, + propertyDescriptorService: PropertyDescriptorRetriever + ): (existingProperties: string[], allowsSensitive: boolean) => Observable { + return (existingProperties: string[], allowsSensitive: boolean) => { + const dialogRequest: NewPropertyDialogRequest = { existingProperties, allowsSensitive }; + const newPropertyDialogReference = this.dialog.open(NewPropertyDialog, { + data: dialogRequest, + panelClass: 'small-dialog' + }); + + return newPropertyDialogReference.componentInstance.newProperty.pipe( + take(1), + switchMap((dialogResponse: NewPropertyDialogResponse) => { + return propertyDescriptorService + .getPropertyDescriptor(id, dialogResponse.name, dialogResponse.sensitive) + .pipe( + take(1), + map((response) => { + newPropertyDialogReference.close(); + + return { + property: dialogResponse.name, + value: null, + descriptor: response.propertyDescriptor + }; + }) + ); + }) + ); + }; + } + + /** + * Returns a function that can be used to pass into a PropertyTable to create a controller service, inline. + * + * @param id id of the component where the inline-create controller service was initiated + * @param controllerServiceCreator service to use to create the controller service and lookup property descriptors + * @param propertyDescriptorService the service to call to get property descriptors + * @param afterServiceCreated OPTIONAL - callback function to possibly dispatch an action after the controller service has been created + * @param processGroupId OPTIONAL - process group id to create the + */ + createNewService( + id: string, + controllerServiceCreator: ControllerServiceCreator, + propertyDescriptorService: PropertyDescriptorRetriever, + processGroupId?: string | null, + afterServiceCreated?: (createdResponse: ControllerServiceEntity) => void + ): (request: InlineServiceCreationRequest) => Observable { + return (request: InlineServiceCreationRequest) => { + const descriptor: PropertyDescriptor = request.descriptor; + + // fetch all services that implement the requested service api + return this.extensionTypesService + .getImplementingControllerServiceTypes( + // @ts-ignore + descriptor.identifiesControllerService, + descriptor.identifiesControllerServiceBundle + ) + .pipe( + take(1), + switchMap((implementingTypesResponse) => { + // show the create controller service dialog with the types that implemented the interface + const createServiceDialogReference = this.dialog.open(CreateControllerService, { + data: { + controllerServiceTypes: implementingTypesResponse.controllerServiceTypes + }, + panelClass: 'medium-dialog' + }); + + return createServiceDialogReference.componentInstance.createControllerService.pipe( + take(1), + switchMap((controllerServiceType) => { + // typically this sequence would be implemented with ngrx actions, however we are + // currently in an edit session, and we need to return both the value (new service id) + // and updated property descriptor so the table renders correctly + const payload: CreateControllerServiceRequest = { + revision: { + clientId: this.client.getClientId(), + version: 0 + }, + controllerServiceType: controllerServiceType.type, + controllerServiceBundle: controllerServiceType.bundle + }; + + if (processGroupId) { + payload.processGroupId = processGroupId; + } + + return controllerServiceCreator.createControllerService(payload).pipe( + take(1), + switchMap((createResponse) => { + // if provided, call the callback function + if (afterServiceCreated) { + afterServiceCreated(createResponse); + } + + // fetch an updated property descriptor + return propertyDescriptorService + .getPropertyDescriptor(id, descriptor.name, false) + .pipe( + take(1), + map((descriptorResponse) => { + createServiceDialogReference.close(); + + return { + value: createResponse.id, + descriptor: descriptorResponse.propertyDescriptor + }; + }) + ); + }), + catchError((error) => { + // TODO - show error + return NEVER; + }) + ); + }) + ); + }) + ); + }; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts index db29215cb9..24203c3827 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/state/extension-types/extension-types.selectors.ts @@ -51,6 +51,11 @@ export const selectFlowAnalysisRuleTypes = createSelector( (state: ExtensionTypesState) => state.flowAnalysisRuleTypes ); +export const selectParameterProviderTypes = createSelector( + selectExtensionTypesState, + (state: ExtensionTypesState) => state.parameterProviderTypes +); + export const selectTypesToIdentifyComponentRestrictions = createSelector( selectExtensionTypesState, (state: ExtensionTypesState) => { 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 dcd872cace..aec81d5933 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 @@ -112,7 +112,7 @@ export interface EditTenantResponse { userGroup?: any; } -export interface CreateControllerServiceRequest { +export interface CreateControllerServiceDialogRequest { controllerServiceTypes: DocumentedType[]; } @@ -306,6 +306,7 @@ export interface ParameterContextReferenceEntity { permissions: Permissions; id: string; component?: ParameterContextReference; + bulletins?: BulletinEntity[]; } export interface ParameterContextReference { @@ -508,6 +509,10 @@ export interface PropertyDescriptor { identifiesControllerServiceBundle?: Bundle; } +export interface PropertyDescriptorEntity { + propertyDescriptor: PropertyDescriptor; +} + export interface Property { property: string; value: string | null; @@ -522,3 +527,18 @@ export interface InlineServiceCreationResponse { value: string; descriptor: PropertyDescriptor; } + +export interface PropertyDescriptorRetriever { + getPropertyDescriptor(id: string, propertyName: string, sensitive: boolean): Observable; +} + +export interface CreateControllerServiceRequest { + processGroupId?: string; + controllerServiceType: string; + controllerServiceBundle: Bundle; + revision: Revision; +} + +export interface ControllerServiceCreator { + createControllerService(createControllerService: CreateControllerServiceRequest): Observable; +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts index fe33870ba1..afa33ccef7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/controller-service-table/controller-service-table.component.ts @@ -55,12 +55,13 @@ import { CurrentUser } from '../../../../state/current-user'; export class ControllerServiceTable { @Input() initialSortColumn: 'name' | 'type' | 'bundle' | 'state' | 'scope' = 'name'; @Input() initialSortDirection: 'asc' | 'desc' = 'asc'; + activeSort: Sort = { + active: this.initialSortColumn, + direction: this.initialSortDirection + }; @Input() set controllerServices(controllerServiceEntities: ControllerServiceEntity[]) { - this.dataSource.data = this.sortEntities(controllerServiceEntities, { - active: this.initialSortColumn, - direction: this.initialSortDirection - }); + this.dataSource.data = this.sortEntities(controllerServiceEntities, this.activeSort); } @Input() selectedServiceId!: string; @@ -277,6 +278,7 @@ export class ControllerServiceTable { } sortData(sort: Sort) { + this.activeSort = sort; this.dataSource.data = this.sortEntities(this.dataSource.data, sort); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/create-controller-service/create-controller-service.component.spec.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/create-controller-service/create-controller-service.component.spec.ts index 951a5d206d..b7ebcc1c17 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/create-controller-service/create-controller-service.component.spec.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/create-controller-service/create-controller-service.component.spec.ts @@ -18,7 +18,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CreateControllerService } from './create-controller-service.component'; -import { CreateControllerServiceRequest, DocumentedType } from '../../../../state/shared'; +import { CreateControllerServiceDialogRequest, DocumentedType } from '../../../../state/shared'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { provideMockStore } from '@ngrx/store/testing'; import { initialState } from '../../../../state/extension-types/extension-types.reducer'; @@ -28,7 +28,7 @@ describe('CreateControllerService', () => { let component: CreateControllerService; let fixture: ComponentFixture; - const data: CreateControllerServiceRequest = { + const data: CreateControllerServiceDialogRequest = { controllerServiceTypes: [ { type: 'org.apache.nifi.services.azure.storage.ADLSCredentialsControllerService', diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/create-controller-service/create-controller-service.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/create-controller-service/create-controller-service.component.ts index 9eef6781c5..56d27ed3db 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/create-controller-service/create-controller-service.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/controller-service/create-controller-service/create-controller-service.component.ts @@ -17,7 +17,7 @@ import { Component, EventEmitter, Inject, Input, Output } from '@angular/core'; import { MAT_DIALOG_DATA } from '@angular/material/dialog'; -import { CreateControllerServiceRequest, DocumentedType } from '../../../../state/shared'; +import { CreateControllerServiceDialogRequest, DocumentedType } from '../../../../state/shared'; import { ExtensionCreation } from '../../extension-creation/extension-creation.component'; import { Observable } from 'rxjs'; import { AsyncPipe } from '@angular/common'; @@ -35,7 +35,7 @@ export class CreateControllerService { controllerServiceTypes: DocumentedType[]; - constructor(@Inject(MAT_DIALOG_DATA) private dialogRequest: CreateControllerServiceRequest) { + constructor(@Inject(MAT_DIALOG_DATA) private dialogRequest: CreateControllerServiceDialogRequest) { this.controllerServiceTypes = dialogRequest.controllerServiceTypes; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/extension-creation/extension-creation.component.ts b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/extension-creation/extension-creation.component.ts index 82d5872b04..ab439d166a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/extension-creation/extension-creation.component.ts +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-frontend/src/main/nifi/src/app/ui/common/extension-creation/extension-creation.component.ts @@ -49,16 +49,17 @@ export class ExtensionCreation { this.selectedType = documentedTypes[0]; } - this.dataSource.data = this.sortEntities(documentedTypes, { - active: this.initialSortColumn, - direction: this.initialSortDirection - }); + this.dataSource.data = this.sortEntities(documentedTypes, this.activeSort); } @Input() componentType!: string; @Input() saving!: boolean; @Input() initialSortColumn: 'type' | 'version' | 'tags' = 'type'; @Input() initialSortDirection: 'asc' | 'desc' = 'asc'; + activeSort: Sort = { + active: this.initialSortColumn, + direction: this.initialSortDirection + }; @Output() extensionTypeSelected: EventEmitter = new EventEmitter(); @@ -151,6 +152,7 @@ export class ExtensionCreation { } sortData(sort: Sort) { + this.activeSort = sort; this.dataSource.data = this.sortEntities(this.dataSource.data, sort); }