[NIFI-13589] improve loading and error handling for jolt UI (#9155)

* [NIFI-13589] improve loading and error handling for jolt UI

* remove unused state

* cleanup more state

* show errors in center of page

This closes #9155
This commit is contained in:
Scott Aslan 2024-08-15 09:05:56 -04:00 committed by GitHub
parent e46c7db1ec
commit 30e8df676e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 64 additions and 36 deletions

View File

@ -97,7 +97,7 @@
(codeMirrorLoaded)="initSpecEditor($event)"></ngx-codemirror> (codeMirrorLoaded)="initSpecEditor($event)"></ngx-codemirror>
</div> </div>
<div class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"> <div class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap">
@if (joltState.validate().saving) { @if (joltState.validate().loading) {
<i <i
class="fa fa-refresh fa-spin mr-2" class="fa fa-refresh fa-spin mr-2"
nifiTooltip nifiTooltip
@ -293,7 +293,7 @@
</div> </div>
<div class="flex flex-1 mt-2 justify-start"> <div class="flex flex-1 mt-2 justify-start">
<div class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap"> <div class="w-full overflow-ellipsis overflow-hidden whitespace-nowrap">
@if (joltState.transform().saving) { @if (joltState.transform().loading) {
<i <i
class="fa fa-refresh fa-spin mr-2" class="fa fa-refresh fa-spin mr-2"
nifiTooltip nifiTooltip
@ -381,9 +381,15 @@
</div> </div>
</form> </form>
} @else { } @else {
<div class="flex flex-1 justify-center items-center"> @if (processorDetailsLoading$ | async) {
<i class="fa fa-warning caution-color mr-2"></i>An error has occurred loading the editor. <div class="h-full flex items-center justify-center">
</div> <mat-spinner color="primary"></mat-spinner>
</div>
} @else if (processorDetailsError()) {
<div class="flex flex-1 justify-center items-center">
<i class="fa fa-warning caution-color mr-2"></i>{{ processorDetailsError() }}
</div>
}
} }
</div> </div>
</div> </div>

View File

@ -26,6 +26,8 @@ import {
selectEditableFromRoute, selectEditableFromRoute,
selectJoltTransformJsonProcessorDetailsState, selectJoltTransformJsonProcessorDetailsState,
selectProcessorDetails, selectProcessorDetails,
selectProcessorDetailsError,
selectProcessorDetailsLoading,
selectProcessorIdFromRoute, selectProcessorIdFromRoute,
selectRevisionFromRoute selectRevisionFromRoute
} from '../state/jolt-transform-json-processor-details/jolt-transform-json-processor-details.selectors'; } from '../state/jolt-transform-json-processor-details/jolt-transform-json-processor-details.selectors';
@ -84,6 +86,8 @@ export class JoltTransformJsonUi implements OnDestroy {
transform: this.store.selectSignal(selectJoltTransformJsonTransformState) transform: this.store.selectSignal(selectJoltTransformJsonTransformState)
}; };
processorDetails$ = this.store.select(selectProcessorDetails); processorDetails$ = this.store.select(selectProcessorDetails);
processorDetailsLoading$ = this.store.select(selectProcessorDetailsLoading);
processorDetailsError = this.store.selectSignal(selectProcessorDetailsError);
editable: boolean = false; editable: boolean = false;
createNew: (existingEntries: string[]) => Observable<MapTableEntry> = createNew: (existingEntries: string[]) => Observable<MapTableEntry> =
this.mapTableHelperService.createNewEntry('Attribute'); this.mapTableHelperService.createNewEntry('Attribute');

View File

@ -39,6 +39,7 @@ import { MatExpansionModule } from '@angular/material/expansion';
import { JoltTransformJsonTransformEffects } from '../state/jolt-transform-json-transform/jolt-transform-json-transform.effects'; import { JoltTransformJsonTransformEffects } from '../state/jolt-transform-json-transform/jolt-transform-json-transform.effects';
import { JoltTransformJsonValidateEffects } from '../state/jolt-transform-json-validate/jolt-transform-json-validate.effects'; import { JoltTransformJsonValidateEffects } from '../state/jolt-transform-json-validate/jolt-transform-json-validate.effects';
import { JoltTransformJsonPropertyEffects } from '../state/jolt-transform-json-property/jolt-transform-json-property.effects'; import { JoltTransformJsonPropertyEffects } from '../state/jolt-transform-json-property/jolt-transform-json-property.effects';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@NgModule({ @NgModule({
declarations: [JoltTransformJsonUi], declarations: [JoltTransformJsonUi],
@ -70,7 +71,8 @@ import { JoltTransformJsonPropertyEffects } from '../state/jolt-transform-json-p
ComponentContext, ComponentContext,
MatExpansionModule, MatExpansionModule,
FormsModule, FormsModule,
MapTable MapTable,
MatProgressSpinnerModule
] ]
}) })
export class JoltTransformJsonUiModule {} export class JoltTransformJsonUiModule {}

View File

@ -16,7 +16,8 @@
*/ */
export interface JoltTransformJsonProcessorDetailsState { export interface JoltTransformJsonProcessorDetailsState {
saving: boolean; loading: boolean;
error: string | null;
processorDetails: ProcessorDetails | null; processorDetails: ProcessorDetails | null;
} }

View File

@ -37,5 +37,5 @@ export const loadProcessorDetailsSuccess = createAction(
export const loadProcessorDetailsFailure = createAction( export const loadProcessorDetailsFailure = createAction(
`${JOLT_TRANSFORM_JSON_PROCESSOR_DETAILS_PREFIX} Load Processor Details Failure`, `${JOLT_TRANSFORM_JSON_PROCESSOR_DETAILS_PREFIX} Load Processor Details Failure`,
props<{ response: HttpErrorResponse }>() props<{ response: string }>()
); );

View File

@ -43,9 +43,19 @@ export class JoltTransformJsonProcessorDetailsEffects {
}) })
), ),
catchError((errorResponse: HttpErrorResponse) => { catchError((errorResponse: HttpErrorResponse) => {
let errorMessage = 'An unspecified error occurred.';
if (errorResponse.status !== 0) {
if (typeof errorResponse.error === 'string') {
errorMessage = errorResponse.error;
} else {
errorMessage = errorResponse.message || `${errorResponse.status}`;
}
}
return of( return of(
loadProcessorDetailsFailure({ loadProcessorDetailsFailure({
response: errorResponse response: errorMessage
}) })
); );
}) })

View File

@ -18,13 +18,15 @@
import { JoltTransformJsonProcessorDetailsState } from './index'; import { JoltTransformJsonProcessorDetailsState } from './index';
import { createReducer, on } from '@ngrx/store'; import { createReducer, on } from '@ngrx/store';
import { import {
loadProcessorDetails,
loadProcessorDetailsFailure, loadProcessorDetailsFailure,
loadProcessorDetailsSuccess, loadProcessorDetailsSuccess,
resetJoltTransformJsonProcessorDetailsState resetJoltTransformJsonProcessorDetailsState
} from './jolt-transform-json-processor-details.actions'; } from './jolt-transform-json-processor-details.actions';
export const initialState: JoltTransformJsonProcessorDetailsState = { export const initialState: JoltTransformJsonProcessorDetailsState = {
saving: false, loading: false,
error: null,
processorDetails: null processorDetails: null
}; };
@ -33,12 +35,20 @@ export const joltTransformJsonProcessorDetailsReducer = createReducer(
on(resetJoltTransformJsonProcessorDetailsState, () => ({ on(resetJoltTransformJsonProcessorDetailsState, () => ({
...initialState ...initialState
})), })),
on(loadProcessorDetails, (state) => ({
...state,
error: null,
loading: true
})),
on(loadProcessorDetailsSuccess, (state, { response }) => ({ on(loadProcessorDetailsSuccess, (state, { response }) => ({
...state, ...state,
processorDetails: response processorDetails: response,
loading: false
})), })),
on(loadProcessorDetailsFailure, (state) => ({ on(loadProcessorDetailsFailure, (state, { response }) => ({
...state, ...state,
processorDetails: null processorDetails: null,
error: response,
loading: false
})) }))
); );

View File

@ -34,9 +34,14 @@ export const selectProcessorDetails = createSelector(
(state: JoltTransformJsonProcessorDetailsState) => state.processorDetails (state: JoltTransformJsonProcessorDetailsState) => state.processorDetails
); );
export const selectSaving = createSelector( export const selectProcessorDetailsLoading = createSelector(
selectJoltTransformJsonProcessorDetailsState, selectJoltTransformJsonProcessorDetailsState,
(state: JoltTransformJsonProcessorDetailsState) => state.saving (state: JoltTransformJsonProcessorDetailsState) => state.loading
);
export const selectProcessorDetailsError = createSelector(
selectJoltTransformJsonProcessorDetailsState,
(state: JoltTransformJsonProcessorDetailsState) => state.error
); );
export const selectProcessorIdFromRoute = createSelector(selectCurrentRoute, (route) => { export const selectProcessorIdFromRoute = createSelector(selectCurrentRoute, (route) => {

View File

@ -18,7 +18,7 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
export interface JoltTransformJsonTransformState { export interface JoltTransformJsonTransformState {
saving: boolean; loading: boolean;
transformationResponse?: TransformJoltSpecSuccess | null; transformationResponse?: TransformJoltSpecSuccess | null;
transformationFailureResponse?: HttpErrorResponse | null; transformationFailureResponse?: HttpErrorResponse | null;
} }

View File

@ -25,7 +25,7 @@ import {
} from './jolt-transform-json-transform.actions'; } from './jolt-transform-json-transform.actions';
export const initialState: JoltTransformJsonTransformState = { export const initialState: JoltTransformJsonTransformState = {
saving: false, loading: false,
transformationResponse: null, transformationResponse: null,
transformationFailureResponse: null transformationFailureResponse: null
}; };
@ -37,16 +37,16 @@ export const joltTransformJsonTransformReducer = createReducer(
})), })),
on(transformJoltSpec, (state) => ({ on(transformJoltSpec, (state) => ({
...state, ...state,
saving: true loading: true
})), })),
on(transformJoltSpecSuccess, (state, { response }) => ({ on(transformJoltSpecSuccess, (state, { response }) => ({
...state, ...state,
saving: false, loading: false,
transformationResponse: response transformationResponse: response
})), })),
on(transformJoltSpecFailure, (state, { response }) => ({ on(transformJoltSpecFailure, (state, { response }) => ({
...state, ...state,
saving: false, loading: false,
transformationResponse: null, transformationResponse: null,
transformationFailureResponse: response transformationFailureResponse: response
})) }))

View File

@ -27,8 +27,3 @@ export const selectJoltTransformJsonTransformState = createSelector(
selectJoltTransformJsonUiState, selectJoltTransformJsonUiState,
(state: JoltTransformJsonUiState) => state[joltTransformJsonTransformFeatureKey] (state: JoltTransformJsonUiState) => state[joltTransformJsonTransformFeatureKey]
); );
export const selectSaving = createSelector(
selectJoltTransformJsonTransformState,
(state: JoltTransformJsonTransformState) => state.saving
);

View File

@ -18,7 +18,7 @@
import { HttpErrorResponse } from '@angular/common/http'; import { HttpErrorResponse } from '@angular/common/http';
export interface JoltTransformJsonValidateState { export interface JoltTransformJsonValidateState {
saving: boolean | null; loading: boolean | null;
validationResponse?: ValidateJoltSpecSuccess | null; validationResponse?: ValidateJoltSpecSuccess | null;
validationFailureResponse?: HttpErrorResponse | null; validationFailureResponse?: HttpErrorResponse | null;
} }

View File

@ -26,7 +26,7 @@ import {
} from './jolt-transform-json-validate.actions'; } from './jolt-transform-json-validate.actions';
export const initialState: JoltTransformJsonValidateState = { export const initialState: JoltTransformJsonValidateState = {
saving: false, loading: false,
validationResponse: null, validationResponse: null,
validationFailureResponse: null validationFailureResponse: null
}; };
@ -38,21 +38,21 @@ export const joltTransformJsonValidateReducer = createReducer(
})), })),
on(validateJoltSpec, (state) => ({ on(validateJoltSpec, (state) => ({
...state, ...state,
saving: true loading: true
})), })),
on(validateJoltSpecSuccess, (state, { response }) => ({ on(validateJoltSpecSuccess, (state, { response }) => ({
...state, ...state,
saving: false, loading: false,
validationResponse: response validationResponse: response
})), })),
on(validateJoltSpecFailure, (state, { response }) => ({ on(validateJoltSpecFailure, (state, { response }) => ({
...state, ...state,
saving: false, loading: false,
validationFailureResponse: response validationFailureResponse: response
})), })),
on(resetValidateJoltSpecState, (state) => ({ on(resetValidateJoltSpecState, (state) => ({
...state, ...state,
saving: null, loading: null,
validationResponse: null, validationResponse: null,
validationFailureResponse: null validationFailureResponse: null
})) }))

View File

@ -27,8 +27,3 @@ export const selectJoltTransformJsonValidateState = createSelector(
selectJoltTransformJsonUiState, selectJoltTransformJsonUiState,
(state: JoltTransformJsonUiState) => state[joltTransformJsonValidateFeatureKey] (state: JoltTransformJsonUiState) => state[joltTransformJsonValidateFeatureKey]
); );
export const selectSaving = createSelector(
selectJoltTransformJsonValidateState,
(state: JoltTransformJsonValidateState) => state.saving
);