NIFI-12684: Handling error responses in the service listing on the canvas (#8321)

* NIFI-12684:
- Handling error responses in the service listing on the canvas.
- Handling error responses when converting a Property value to a Parameter.

* NIFI-12684:
- Introducing parameter helper to remove duplicated logic and error handling when fetching parameters and converting properties to parameters.

This closes #8321
This commit is contained in:
Matt Gilman 2024-02-01 11:47:52 -05:00 committed by GitHub
parent 7edf0d4e2a
commit 102daa15f8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 330 additions and 332 deletions

View File

@ -48,8 +48,8 @@ export class ControllerServiceService implements ControllerServiceCreator, Prope
);
}
getBreadcrumbs(processGroupId: string): Observable<any> {
return this.httpClient.get(`${ControllerServiceService.API}/flow/process-groups/${processGroupId}/breadcrumbs`);
getFlow(processGroupId: string): Observable<any> {
return this.httpClient.get(`${ControllerServiceService.API}/flow/process-groups/${processGroupId}`);
}
getControllerService(id: string): Observable<any> {

View File

@ -0,0 +1,162 @@
/*
* 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, EMPTY, filter, map, Observable, switchMap, take, takeUntil, tap } from 'rxjs';
import { Store } from '@ngrx/store';
import { HttpErrorResponse } from '@angular/common/http';
import { NiFiState } from '../../../state';
import { ParameterService } from './parameter.service';
import { Client } from '../../../service/client.service';
import { EditParameterRequest, EditParameterResponse, Parameter, ParameterEntity } from '../../../state/shared';
import { EditParameterDialog } from '../../../ui/common/edit-parameter-dialog/edit-parameter-dialog.component';
import { selectParameterSaving, selectParameterState } from '../state/parameter/parameter.selectors';
import { ParameterState } from '../state/parameter';
import * as ErrorActions from '../../../state/error/error.actions';
import * as ParameterActions from '../state/parameter/parameter.actions';
import { FlowService } from './flow.service';
@Injectable({
providedIn: 'root'
})
export class ParameterHelperService {
constructor(
private dialog: MatDialog,
private store: Store<NiFiState>,
private flowService: FlowService,
private parameterService: ParameterService,
private client: Client
) {}
/**
* Returns a function that can be used to pass into a PropertyTable to retrieve available Parameters.
*
* @param parameterContextId the current Parameter Context id
*/
getParameters(parameterContextId: string): (sensitive: boolean) => Observable<Parameter[]> {
return (sensitive: boolean) => {
return this.flowService.getParameterContext(parameterContextId).pipe(
take(1),
catchError((errorResponse: HttpErrorResponse) => {
this.store.dispatch(ErrorActions.snackBarError({ error: errorResponse.error }));
// consider the error handled and allow the user to reattempt the action
return EMPTY;
}),
map((response) => response.component.parameters),
map((parameterEntities) => {
return parameterEntities
.map((parameterEntity: ParameterEntity) => parameterEntity.parameter)
.filter((parameter: Parameter) => parameter.sensitive == sensitive);
})
);
};
}
/**
* Returns a function that can be used to pass into a PropertyTable to convert a Property into a Parameter, inline.
*
* @param parameterContextId the current Parameter Context id
*/
convertToParameter(
parameterContextId: string
): (name: string, sensitive: boolean, value: string | null) => Observable<string> {
return (name: string, sensitive: boolean, value: string | null) => {
return this.parameterService.getParameterContext(parameterContextId, false).pipe(
catchError((errorResponse: HttpErrorResponse) => {
this.store.dispatch(ErrorActions.snackBarError({ error: errorResponse.error }));
// consider the error handled and allow the user to reattempt the action
return EMPTY;
}),
switchMap((parameterContextEntity) => {
const existingParameters: string[] = parameterContextEntity.component.parameters.map(
(parameterEntity: ParameterEntity) => parameterEntity.parameter.name
);
const convertToParameterDialogRequest: EditParameterRequest = {
parameter: {
name,
value,
sensitive,
description: ''
},
existingParameters
};
const convertToParameterDialogReference = this.dialog.open(EditParameterDialog, {
data: convertToParameterDialogRequest,
panelClass: 'medium-dialog'
});
convertToParameterDialogReference.componentInstance.saving$ =
this.store.select(selectParameterSaving);
convertToParameterDialogReference.componentInstance.cancel.pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
tap(() => ParameterActions.stopPollingParameterContextUpdateRequest())
);
return convertToParameterDialogReference.componentInstance.editParameter.pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
switchMap((dialogResponse: EditParameterResponse) => {
this.store.dispatch(
ParameterActions.submitParameterContextUpdateRequest({
request: {
id: parameterContextId,
payload: {
revision: this.client.getRevision(parameterContextEntity),
component: {
id: parameterContextEntity.id,
parameters: [{ parameter: dialogResponse.parameter }]
}
}
}
})
);
return this.store.select(selectParameterState).pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
tap((parameterState: ParameterState) => {
if (parameterState.error) {
// if the convert to parameter sequence stores an error,
// throw it to avoid the completion mapping logic below
throw new Error(parameterState.error);
}
}),
filter((parameterState: ParameterState) => !parameterState.saving),
map(() => {
convertToParameterDialogReference.close();
return `#{${dialogResponse.parameter.name}}`;
}),
catchError((error) => {
convertToParameterDialogReference.close();
// show the error in the snack and complete the edit to reset it's state
this.store.dispatch(ErrorActions.snackBarError({ error: error.message }));
this.store.dispatch(ParameterActions.editParameterContextComplete());
// consider the error handled and allow the user to reattempt the action
return EMPTY;
})
);
})
);
})
);
};
}
}

View File

@ -45,8 +45,8 @@ export const loadControllerServicesSuccess = createAction(
props<{ response: LoadControllerServicesResponse }>()
);
export const controllerServicesApiError = createAction(
'[Controller Services] Load Controller Service Error',
export const controllerServicesBannerApiError = createAction(
'[Controller Services] Controller Services Banner Api Error',
props<{ error: string }>()
);

View File

@ -18,7 +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, of, switchMap, take, takeUntil, tap } from 'rxjs';
import { catchError, combineLatest, 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,25 +30,24 @@ import { EditControllerService } from '../../../../ui/common/controller-service/
import {
ComponentType,
ControllerServiceReferencingComponent,
EditParameterRequest,
EditParameterResponse,
Parameter,
ParameterEntity,
UpdateControllerServiceRequest
} from '../../../../state/shared';
import { Router } from '@angular/router';
import { ExtensionTypesService } from '../../../../service/extension-types.service';
import { selectCurrentProcessGroupId, selectSaving } from './controller-services.selectors';
import {
selectCurrentProcessGroupId,
selectParameterContext,
selectSaving,
selectStatus
} from './controller-services.selectors';
import { ControllerServiceService } from '../../service/controller-service.service';
import { selectCurrentParameterContext } from '../flow/flow.selectors';
import { FlowService } from '../../service/flow.service';
import { EditParameterDialog } from '../../../../ui/common/edit-parameter-dialog/edit-parameter-dialog.component';
import { selectParameterSaving } from '../parameter/parameter.selectors';
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';
import * as ErrorActions from '../../../../state/error/error.actions';
import { ErrorHelper } from '../../../../service/error-helper.service';
import { HttpErrorResponse } from '@angular/common/http';
import { ParameterHelperService } from '../../service/parameter-helper.service';
@Injectable()
export class ControllerServicesEffects {
@ -58,42 +57,48 @@ export class ControllerServicesEffects {
private client: Client,
private controllerServiceService: ControllerServiceService,
private flowService: FlowService,
private parameterService: ParameterService,
private extensionTypesService: ExtensionTypesService,
private errorHelper: ErrorHelper,
private dialog: MatDialog,
private router: Router,
private propertyTableHelperService: PropertyTableHelperService
private propertyTableHelperService: PropertyTableHelperService,
private parameterHelperService: ParameterHelperService
) {}
loadControllerServices$ = createEffect(() =>
this.actions$.pipe(
ofType(ControllerServicesActions.loadControllerServices),
map((action) => action.request),
switchMap((request) =>
concatLatestFrom(() => this.store.select(selectStatus)),
switchMap(([request, status]) =>
combineLatest([
this.controllerServiceService.getControllerServices(request.processGroupId),
this.controllerServiceService.getBreadcrumbs(request.processGroupId)
this.controllerServiceService.getFlow(request.processGroupId)
]).pipe(
map(([controllerServicesResponse, breadcrumbsResponse]) =>
map(([controllerServicesResponse, flowResponse]) =>
ControllerServicesActions.loadControllerServicesSuccess({
response: {
processGroupId: breadcrumbsResponse.id,
processGroupId: flowResponse.processGroupFlow.id,
controllerServices: controllerServicesResponse.controllerServices,
loadedTimestamp: controllerServicesResponse.currentTime,
breadcrumb: breadcrumbsResponse
breadcrumb: flowResponse.processGroupFlow.breadcrumb,
parameterContext: flowResponse.processGroupFlow.parameterContext ?? null
}
})
),
catchError((error) =>
of(
ControllerServicesActions.controllerServicesApiError({
error: error.error
catchError((errorResponse: HttpErrorResponse) => {
if (status === 'success') {
if (this.errorHelper.showErrorInContext(errorResponse.status)) {
return of(ErrorActions.snackBarError({ error: errorResponse.error }));
} else {
return of(this.errorHelper.fullScreenError(errorResponse));
}
} else {
return of(this.errorHelper.fullScreenError(errorResponse));
}
})
)
)
)
)
)
);
openNewControllerServiceDialog$ = createEffect(
@ -149,16 +154,13 @@ export class ControllerServicesEffects {
}
})
),
catchError((error) =>
of(
ControllerServicesActions.controllerServicesApiError({
error: error.error
catchError((errorResponse: HttpErrorResponse) => {
this.dialog.closeAll();
return of(ErrorActions.snackBarError({ error: errorResponse.error }));
})
)
)
)
)
)
);
createControllerServiceSuccess$ = createEffect(
@ -191,7 +193,7 @@ export class ControllerServicesEffects {
ofType(ControllerServicesActions.openConfigureControllerServiceDialog),
map((action) => action.request),
concatLatestFrom(() => [
this.store.select(selectCurrentParameterContext),
this.store.select(selectParameterContext),
this.store.select(selectCurrentProcessGroupId)
]),
tap(([request, parameterContext, processGroupId]) => {
@ -242,17 +244,9 @@ export class ControllerServicesEffects {
};
if (parameterContext != null) {
editDialogReference.componentInstance.getParameters = (sensitive: boolean) => {
return this.flowService.getParameterContext(parameterContext.id).pipe(
take(1),
map((response) => response.component.parameters),
map((parameterEntities) => {
return parameterEntities
.map((parameterEntity: ParameterEntity) => parameterEntity.parameter)
.filter((parameter: Parameter) => parameter.sensitive == sensitive);
})
editDialogReference.componentInstance.getParameters = this.parameterHelperService.getParameters(
parameterContext.id
);
};
editDialogReference.componentInstance.parameterContext = parameterContext;
editDialogReference.componentInstance.goToParameter = () => {
@ -260,74 +254,8 @@ export class ControllerServicesEffects {
goTo(commands, 'Parameter');
};
editDialogReference.componentInstance.convertToParameter = (
name: string,
sensitive: boolean,
value: string | null
) => {
return this.parameterService.getParameterContext(parameterContext.id, false).pipe(
switchMap((parameterContextEntity) => {
const existingParameters: string[] =
parameterContextEntity.component.parameters.map(
(parameterEntity: ParameterEntity) => parameterEntity.parameter.name
);
const convertToParameterDialogRequest: EditParameterRequest = {
parameter: {
name,
value,
sensitive,
description: ''
},
existingParameters
};
const convertToParameterDialogReference = this.dialog.open(EditParameterDialog, {
data: convertToParameterDialogRequest,
panelClass: 'medium-dialog'
});
convertToParameterDialogReference.componentInstance.saving$ =
this.store.select(selectParameterSaving);
convertToParameterDialogReference.componentInstance.cancel.pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
tap(() => ParameterActions.stopPollingParameterContextUpdateRequest())
);
return convertToParameterDialogReference.componentInstance.editParameter.pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
switchMap((dialogResponse: EditParameterResponse) => {
this.store.dispatch(
ParameterActions.submitParameterContextUpdateRequest({
request: {
id: parameterContext.id,
payload: {
revision: this.client.getRevision(parameterContextEntity),
component: {
id: parameterContextEntity.id,
parameters: [{ parameter: dialogResponse.parameter }]
}
}
}
})
);
return this.store.select(selectParameterSaving).pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
filter((parameterSaving) => parameterSaving === false),
map(() => {
convertToParameterDialogReference.close();
return `#{${dialogResponse.parameter.name}}`;
})
);
})
);
}),
catchError(() => {
// TODO handle error
return NEVER;
})
);
};
editDialogReference.componentInstance.convertToParameter =
this.parameterHelperService.convertToParameter(parameterContext.id);
}
editDialogReference.componentInstance.goToService = (serviceId: string) => {
@ -341,8 +269,8 @@ export class ControllerServicesEffects {
];
goTo(commands, 'Controller Service');
},
error: () => {
// TODO - handle error
error: (errorResponse: HttpErrorResponse) => {
this.store.dispatch(ErrorActions.snackBarError({ error: errorResponse.error }));
}
});
};
@ -410,15 +338,28 @@ export class ControllerServicesEffects {
}
})
),
catchError((error) =>
of(
ControllerServicesActions.controllerServicesApiError({
error: error.error
catchError((errorResponse: HttpErrorResponse) => {
if (this.errorHelper.showErrorInContext(errorResponse.status)) {
return of(
ControllerServicesActions.controllerServicesBannerApiError({
error: errorResponse.error
})
);
} else {
this.dialog.getDialogById(request.id)?.close('ROUTED');
return of(this.errorHelper.fullScreenError(errorResponse));
}
})
)
)
)
)
);
controllerServicesBannerApiError$ = createEffect(() =>
this.actions$.pipe(
ofType(ControllerServicesActions.controllerServicesBannerApiError),
map((action) => action.error),
switchMap((error) => of(ErrorActions.addBannerError({ error })))
)
);
@ -558,12 +499,8 @@ export class ControllerServicesEffects {
}
})
),
catchError((error) =>
of(
ControllerServicesActions.controllerServicesApiError({
error: error.error
})
)
catchError((errorResponse: HttpErrorResponse) =>
of(ErrorActions.snackBarError({ error: errorResponse.error }))
)
)
)

View File

@ -19,7 +19,7 @@ import { createReducer, on } from '@ngrx/store';
import {
configureControllerService,
configureControllerServiceSuccess,
controllerServicesApiError,
controllerServicesBannerApiError,
createControllerService,
createControllerServiceSuccess,
deleteControllerService,
@ -47,9 +47,9 @@ export const initialState: ControllerServicesState = {
name: ''
}
},
parameterContext: null,
saving: false,
loadedTimestamp: '',
error: null,
status: 'pending'
};
@ -67,15 +67,13 @@ export const controllerServicesReducer = createReducer(
processGroupId: response.processGroupId,
controllerServices: response.controllerServices,
breadcrumb: response.breadcrumb,
parameterContext: response.parameterContext,
loadedTimestamp: response.loadedTimestamp,
error: null,
status: 'success' as const
})),
on(controllerServicesApiError, (state, { error }) => ({
on(controllerServicesBannerApiError, (state) => ({
...state,
saving: false,
error,
status: 'error' as const
saving: false
})),
on(createControllerService, configureControllerService, deleteControllerService, (state) => ({
...state,

View File

@ -31,11 +31,21 @@ export const selectSaving = createSelector(
(state: ControllerServicesState) => state.saving
);
export const selectStatus = createSelector(
selectControllerServicesState,
(state: ControllerServicesState) => state.status
);
export const selectCurrentProcessGroupId = createSelector(
selectControllerServicesState,
(state: ControllerServicesState) => state.processGroupId
);
export const selectParameterContext = createSelector(
selectControllerServicesState,
(state: ControllerServicesState) => state.parameterContext
);
export const selectProcessGroupIdFromRoute = createSelector(selectCurrentRoute, (route) => {
if (route) {
// always select the process group from the route

View File

@ -15,7 +15,7 @@
* limitations under the License.
*/
import { ControllerServiceEntity } from '../../../../state/shared';
import { ControllerServiceEntity, ParameterContextReferenceEntity } from '../../../../state/shared';
import { BreadcrumbEntity } from '../shared';
export const controllerServicesFeatureKey = 'controllerServiceListing';
@ -28,6 +28,7 @@ export interface LoadControllerServicesResponse {
processGroupId: string;
breadcrumb: BreadcrumbEntity;
controllerServices: ControllerServiceEntity[];
parameterContext: ParameterContextReferenceEntity | null;
loadedTimestamp: string;
}
@ -65,8 +66,8 @@ export interface ControllerServicesState {
processGroupId: string;
breadcrumb: BreadcrumbEntity;
controllerServices: ControllerServiceEntity[];
parameterContext: ParameterContextReferenceEntity | null;
saving: boolean;
loadedTimestamp: string;
error: string | null;
status: 'pending' | 'loading' | 'error' | 'success';
status: 'pending' | 'loading' | 'success';
}

View File

@ -19,7 +19,6 @@ import { Injectable } from '@angular/core';
import { FlowService } from '../../service/flow.service';
import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects';
import * as FlowActions from './flow.actions';
import * as ParameterActions from '../parameter/parameter.actions';
import * as StatusHistoryActions from '../../../../state/status-history/status-history.actions';
import {
asyncScheduler,
@ -30,7 +29,6 @@ import {
interval,
map,
mergeMap,
NEVER,
Observable,
of,
switchMap,
@ -65,13 +63,7 @@ import { ConnectionManager } from '../../service/manager/connection-manager.serv
import { MatDialog } from '@angular/material/dialog';
import { CreatePort } from '../../ui/canvas/items/port/create-port/create-port.component';
import { EditPort } from '../../ui/canvas/items/port/edit-port/edit-port.component';
import {
ComponentType,
EditParameterRequest,
EditParameterResponse,
Parameter,
ParameterEntity
} from '../../../../state/shared';
import { ComponentType } from '../../../../state/shared';
import { Router } from '@angular/router';
import { Client } from '../../../../service/client.service';
import { CanvasUtils } from '../../service/canvas-utils.service';
@ -87,13 +79,10 @@ 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 { 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';
import { ParameterHelperService } from '../../service/parameter-helper.service';
@Injectable()
export class FlowEffects {
@ -101,9 +90,7 @@ export class FlowEffects {
private actions$: Actions,
private store: Store<NiFiState>,
private flowService: FlowService,
private extensionTypesService: ExtensionTypesService,
private controllerServiceService: ControllerServiceService,
private parameterService: ParameterService,
private client: Client,
private canvasUtils: CanvasUtils,
private canvasView: CanvasView,
@ -111,7 +98,8 @@ export class FlowEffects {
private connectionManager: ConnectionManager,
private router: Router,
private dialog: MatDialog,
private propertyTableHelperService: PropertyTableHelperService
private propertyTableHelperService: PropertyTableHelperService,
private parameterHelperService: ParameterHelperService
) {}
reloadFlow$ = createEffect(() =>
@ -874,17 +862,9 @@ export class FlowEffects {
};
if (parameterContext != null) {
editDialogReference.componentInstance.getParameters = (sensitive: boolean) => {
return this.flowService.getParameterContext(parameterContext.id).pipe(
take(1),
map((response) => response.component.parameters),
map((parameterEntities) => {
return parameterEntities
.map((parameterEntity: ParameterEntity) => parameterEntity.parameter)
.filter((parameter: Parameter) => parameter.sensitive == sensitive);
})
editDialogReference.componentInstance.getParameters = this.parameterHelperService.getParameters(
parameterContext.id
);
};
editDialogReference.componentInstance.parameterContext = parameterContext;
editDialogReference.componentInstance.goToParameter = () => {
@ -892,74 +872,8 @@ export class FlowEffects {
goTo(commands, 'Parameter');
};
editDialogReference.componentInstance.convertToParameter = (
name: string,
sensitive: boolean,
value: string | null
) => {
return this.parameterService.getParameterContext(parameterContext.id, false).pipe(
switchMap((parameterContextEntity) => {
const existingParameters: string[] =
parameterContextEntity.component.parameters.map(
(parameterEntity: ParameterEntity) => parameterEntity.parameter.name
);
const convertToParameterDialogRequest: EditParameterRequest = {
parameter: {
name,
value,
sensitive,
description: ''
},
existingParameters
};
const convertToParameterDialogReference = this.dialog.open(EditParameterDialog, {
data: convertToParameterDialogRequest,
panelClass: 'medium-dialog'
});
convertToParameterDialogReference.componentInstance.saving$ =
this.store.select(selectParameterSaving);
convertToParameterDialogReference.componentInstance.cancel.pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
tap(() => ParameterActions.stopPollingParameterContextUpdateRequest())
);
return convertToParameterDialogReference.componentInstance.editParameter.pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
switchMap((dialogResponse: EditParameterResponse) => {
this.store.dispatch(
ParameterActions.submitParameterContextUpdateRequest({
request: {
id: parameterContext.id,
payload: {
revision: this.client.getRevision(parameterContextEntity),
component: {
id: parameterContextEntity.id,
parameters: [{ parameter: dialogResponse.parameter }]
}
}
}
})
);
return this.store.select(selectParameterSaving).pipe(
takeUntil(convertToParameterDialogReference.afterClosed()),
filter((parameterSaving) => parameterSaving === false),
map(() => {
convertToParameterDialogReference.close();
return `#{${dialogResponse.parameter.name}}`;
})
);
})
);
}),
catchError(() => {
// TODO handle error
return NEVER;
})
);
};
editDialogReference.componentInstance.convertToParameter =
this.parameterHelperService.convertToParameter(parameterContext.id);
}
editDialogReference.componentInstance.goToService = (serviceId: string) => {

View File

@ -22,4 +22,5 @@ export const parameterFeatureKey = 'parameter';
export interface ParameterState {
updateRequestEntity: ParameterContextUpdateRequestEntity | null;
saving: boolean;
error: string | null;
}

View File

@ -20,10 +20,11 @@ import { Actions, concatLatestFrom, createEffect, ofType } from '@ngrx/effects';
import * as ParameterActions from './parameter.actions';
import { Store } from '@ngrx/store';
import { CanvasState } from '../index';
import { asyncScheduler, catchError, from, interval, map, NEVER, of, switchMap, takeUntil, tap } from 'rxjs';
import { asyncScheduler, catchError, from, interval, map, NEVER, of, switchMap, takeUntil } from 'rxjs';
import { ParameterContextUpdateRequest } from '../../../../state/shared';
import { selectUpdateRequest } from './parameter.selectors';
import { ParameterService } from '../../service/parameter.service';
import { HttpErrorResponse } from '@angular/common/http';
@Injectable()
export class ParameterEffects {
@ -46,12 +47,8 @@ export class ParameterEffects {
}
})
),
catchError((error) =>
of(
ParameterActions.parameterApiError({
error: error.error
})
)
catchError((errorResponse: HttpErrorResponse) =>
of(ParameterActions.parameterApiError({ error: errorResponse.error }))
)
)
)
@ -140,12 +137,22 @@ export class ParameterEffects {
this.actions$.pipe(
ofType(ParameterActions.deleteParameterContextUpdateRequest),
concatLatestFrom(() => this.store.select(selectUpdateRequest)),
tap(([, updateRequest]) => {
switchMap(([, updateRequest]) => {
if (updateRequest) {
this.parameterService.deleteParameterContextUpdate(updateRequest.request).subscribe();
return from(this.parameterService.deleteParameterContextUpdate(updateRequest.request)).pipe(
map(() => ParameterActions.editParameterContextComplete()),
catchError((error) =>
of(
ParameterActions.parameterApiError({
error: error.error
})
)
)
);
} else {
return of(ParameterActions.editParameterContextComplete());
}
}),
switchMap(() => of(ParameterActions.editParameterContextComplete()))
})
)
);
}

View File

@ -19,6 +19,7 @@ import { createReducer, on } from '@ngrx/store';
import { ParameterState } from './index';
import {
editParameterContextComplete,
parameterApiError,
pollParameterContextUpdateRequestSuccess,
submitParameterContextUpdateRequest,
submitParameterContextUpdateRequestSuccess
@ -26,7 +27,8 @@ import {
export const initialState: ParameterState = {
updateRequestEntity: null,
saving: false
saving: false,
error: null
};
export const parameterReducer = createReducer(
@ -35,13 +37,15 @@ export const parameterReducer = createReducer(
...state,
saving: true
})),
on(parameterApiError, (state, { error }) => ({
...state,
error
})),
on(submitParameterContextUpdateRequestSuccess, pollParameterContextUpdateRequestSuccess, (state, { response }) => ({
...state,
updateRequestEntity: response.requestEntity
})),
on(editParameterContextComplete, (state) => ({
...state,
saving: false,
updateRequestEntity: null
on(editParameterContextComplete, () => ({
...initialState
}))
);

View File

@ -35,14 +35,14 @@
<i class="fa fa-plus"></i>
</button>
</div>
<div class="flex-1">
<div class="flex-1" *ngIf="flowConfiguration$ | async; let flowConfiguration">
<controller-service-table
[selectedServiceId]="selectedServiceId$ | async"
[controllerServices]="serviceState.controllerServices"
[formatScope]="formatScope(serviceState.breadcrumb)"
[definedByCurrentGroup]="definedByCurrentGroup(serviceState.breadcrumb)"
[currentUser]="(currentUser$ | async)!"
[flowConfiguration]="(flowConfiguration$ | async)!"
[flowConfiguration]="flowConfiguration"
[canModifyParent]="canModifyParent(serviceState.breadcrumb)"
(selectControllerService)="selectControllerService($event)"
(configureControllerService)="configureControllerService($event)"

View File

@ -39,7 +39,7 @@ import {
selectControllerService
} from '../../state/controller-services/controller-services.actions';
import { initialState } from '../../state/controller-services/controller-services.reducer';
import { ControllerServiceEntity } from '../../../../state/shared';
import { ControllerServiceEntity, isDefinedAndNotNull } from '../../../../state/shared';
import { BreadcrumbEntity } from '../../state/shared';
import { selectCurrentUser } from '../../../../state/current-user/current-user.selectors';
import { selectFlowConfiguration } from '../../../../state/flow-configuration/flow-configuration.selectors';
@ -56,7 +56,7 @@ export class ControllerServices implements OnInit, OnDestroy {
serviceState$ = this.store.select(selectControllerServicesState);
selectedServiceId$ = this.store.select(selectControllerServiceIdFromRoute);
currentUser$ = this.store.select(selectCurrentUser);
flowConfiguration$ = this.store.select(selectFlowConfiguration);
flowConfiguration$ = this.store.select(selectFlowConfiguration).pipe(isDefinedAndNotNull());
private currentProcessGroupId!: string;

View File

@ -441,9 +441,9 @@ export class ManagementControllerServicesEffects {
}
})
),
catchError((errorResponse: HttpErrorResponse) => {
return of(ErrorActions.snackBarError({ error: errorResponse.error }));
})
catchError((errorResponse: HttpErrorResponse) =>
of(ErrorActions.snackBarError({ error: errorResponse.error }))
)
)
)
)

View File

@ -26,62 +26,62 @@ import {
} from './index';
export const resetEnableControllerServiceState = createAction(
'[Enable Controller Service] Reset Enable Controller Service State'
'[Controller Service State] Reset Enable Controller Service State'
);
export const setControllerService = createAction(
'[Enable Controller Service] Set Controller Service',
'[Controller Service State] Set Controller Service',
props<{
request: SetControllerServiceRequest;
}>()
);
export const submitEnableRequest = createAction(
'[Enable Controller Service] Submit Enable Request',
'[Controller Service State] Submit Enable Request',
props<{
request: SetEnableControllerServiceRequest;
}>()
);
export const submitDisableRequest = createAction('[Enable Controller Service] Submit Disable Request');
export const submitDisableRequest = createAction('[Controller Service State] Submit Disable Request');
export const setEnableControllerService = createAction('[Enable Controller Service] Set Enable Controller Service');
export const setEnableControllerService = createAction('[Controller Service State] Set Enable Controller Service');
export const setEnableControllerServiceSuccess = createAction(
'[Enable Controller Service] Set Enable Controller Service Success',
'[Controller Service State] Set Enable Controller Service Success',
props<{
response: ControllerServiceStepResponse;
}>()
);
export const updateReferencingServices = createAction('[Enable Controller Service] Update Referencing Services');
export const updateReferencingServices = createAction('[Controller Service State] Update Referencing Services');
export const updateReferencingServicesSuccess = createAction(
'[Enable Controller Service] Update Referencing Services Success',
'[Controller Service State] Update Referencing Services Success',
props<{
response: ReferencingComponentsStepResponse;
}>()
);
export const updateReferencingComponents = createAction('[Enable Controller Service] Update Referencing Components');
export const updateReferencingComponents = createAction('[Controller Service State] Update Referencing Components');
export const updateReferencingComponentsSuccess = createAction(
'[Enable Controller Service] Update Referencing Components Success',
'[Controller Service State] Update Referencing Components Success',
props<{
response: ReferencingComponentsStepResponse;
}>()
);
export const startPollingControllerService = createAction(
'[Enable Controller Service] Start Polling Controller Service'
'[Controller Service State] Start Polling Controller Service'
);
export const stopPollingControllerService = createAction('[Enable Controller Service] Stop Polling Controller Service');
export const stopPollingControllerService = createAction('[Controller Service State] Stop Polling Controller Service');
export const pollControllerService = createAction('[Enable Controller Service] Poll Controller Service');
export const pollControllerService = createAction('[Controller Service State] Poll Controller Service');
export const pollControllerServiceSuccess = createAction(
'[Enable Controller Service] Poll Controller Service Success',
'[Controller Service State] Poll Controller Service Success',
props<{
response: ControllerServiceStepResponse;
previousStep: SetEnableStep;
@ -89,25 +89,14 @@ export const pollControllerServiceSuccess = createAction(
);
export const setEnableStepFailure = createAction(
'[Enable Controller Service] Set Enable Step Failure',
'[Controller Service State] Set Enable Step Failure',
props<{
response: SetEnableStepFailure;
}>()
);
export const controllerServiceApiError = createAction(
'[Enable Controller Service] Controller Service Api Error',
props<{
error: string;
}>()
);
export const clearControllerServiceApiError = createAction(
'[Enable Controller Service] Clear Controller Service Api Error'
);
export const showOkDialog = createAction(
'[Enable Controller Service] Show Ok Dialog',
'[Controller Service State] Show Ok Dialog',
props<{
title: string;
message: string;

View File

@ -87,14 +87,11 @@ export class ControllerServiceStateEffects {
this.actions$.pipe(
ofType(ControllerServiceActions.setEnableControllerService),
concatLatestFrom(() => [
this.store.select(selectControllerService),
this.store.select(selectControllerService).pipe(isDefinedAndNotNull()),
this.store.select(selectControllerServiceSetEnableRequest)
]),
switchMap(([, controllerService, setEnableRequest]) => {
if (controllerService) {
return from(
this.controllerServiceStateService.setEnable(controllerService, setEnableRequest.enable)
).pipe(
switchMap(([, controllerService, setEnableRequest]) =>
from(this.controllerServiceStateService.setEnable(controllerService, setEnableRequest.enable)).pipe(
map((response) =>
ControllerServiceActions.setEnableControllerServiceSuccess({
response: {
@ -113,16 +110,8 @@ export class ControllerServiceStateEffects {
})
)
)
);
} else {
return of(
ControllerServiceActions.showOkDialog({
title: 'Enable Service',
message: 'Controller Service not initialized'
})
);
}
})
)
)
)
);

View File

@ -18,8 +18,6 @@
import { createReducer, on } from '@ngrx/store';
import { ControllerServiceState, SetEnableStep } from './index';
import {
clearControllerServiceApiError,
controllerServiceApiError,
setEnableStepFailure,
pollControllerServiceSuccess,
resetEnableControllerServiceState,
@ -39,7 +37,6 @@ export const initialState: ControllerServiceState = {
scope: 'SERVICE_ONLY'
},
controllerService: null,
error: null,
status: 'pending'
};
@ -91,15 +88,5 @@ export const controllerServiceStateReducer = createReducer(
error: response
},
status: 'error' as const
})),
on(controllerServiceApiError, (state, { error }) => ({
...state,
error: error,
status: 'error' as const
})),
on(clearControllerServiceApiError, (state) => ({
...state,
error: null,
status: 'pending' as const
}))
);

View File

@ -76,6 +76,5 @@ export interface SetEnableRequest {
export interface ControllerServiceState {
setEnableRequest: SetEnableRequest;
controllerService: ControllerServiceEntity | null;
error: string | null;
status: 'pending' | 'loading' | 'error' | 'success';
}