UI: Entities version create/restore progress and error handling
This commit is contained in:
parent
6ea1ac0c8e
commit
9a893dd330
@ -53,12 +53,14 @@ import { forkJoin, Observable, of, ReplaySubject, Subject, throwError, timer } f
|
||||
import { CancelAnimationFrame } from '@core/services/raf.service';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
import {
|
||||
createLabelFromDatasource, createLabelFromPattern,
|
||||
deepClone, flatFormattedData,
|
||||
createLabelFromPattern,
|
||||
deepClone,
|
||||
flatFormattedData,
|
||||
formattedDataFormDatasourceData,
|
||||
isDefined,
|
||||
isDefinedAndNotNull,
|
||||
isEqual
|
||||
isEqual,
|
||||
parseHttpErrorMessage
|
||||
} from '@core/utils';
|
||||
import { EntityId } from '@app/shared/models/id/entity-id';
|
||||
import * as moment_ from 'moment';
|
||||
@ -801,10 +803,10 @@ export class WidgetSubscription implements IWidgetSubscription {
|
||||
this.rpcErrorText = 'Request Timeout.';
|
||||
} else {
|
||||
this.rpcErrorText = 'Error : ' + rejection.status + ' - ' + rejection.statusText;
|
||||
const error = this.extractRejectionErrorText(rejection);
|
||||
const error = parseHttpErrorMessage(rejection, this.ctx.translate);
|
||||
if (error) {
|
||||
this.rpcErrorText += '</br>';
|
||||
this.rpcErrorText += error;
|
||||
this.rpcErrorText += error.message;
|
||||
}
|
||||
}
|
||||
this.callbacks.onRpcFailed(this);
|
||||
@ -816,40 +818,6 @@ export class WidgetSubscription implements IWidgetSubscription {
|
||||
}
|
||||
}
|
||||
|
||||
private extractRejectionErrorText(rejection: HttpErrorResponse) {
|
||||
let error = null;
|
||||
if (rejection.error) {
|
||||
error = rejection.error;
|
||||
try {
|
||||
error = rejection.error ? JSON.parse(rejection.error) : null;
|
||||
} catch (e) {}
|
||||
}
|
||||
if (error && !error.message) {
|
||||
error = this.prepareMessageFromData(error);
|
||||
} else if (error && error.message) {
|
||||
error = error.message;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
private prepareMessageFromData(data) {
|
||||
if (typeof data === 'object' && data.constructor === ArrayBuffer) {
|
||||
const msg = String.fromCharCode.apply(null, new Uint8Array(data));
|
||||
try {
|
||||
const msgObj = JSON.parse(msg);
|
||||
if (msgObj.message) {
|
||||
return msgObj.message;
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
} catch (e) {
|
||||
return msg;
|
||||
}
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
update(isTimewindowTypeChanged = false) {
|
||||
if (this.type !== widgetType.rpc) {
|
||||
this.widgetTimewindowChangedSubject.next(this.timeWindowConfig);
|
||||
|
||||
@ -38,10 +38,11 @@ import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.m
|
||||
import { select, Store } from '@ngrx/store';
|
||||
import { AppState } from '@core/core.state';
|
||||
import { selectIsUserLoaded } from '@core/auth/auth.selectors';
|
||||
import { catchError, finalize, switchMap, takeWhile, tap } from 'rxjs/operators';
|
||||
import { catchError, finalize, map, switchMap, takeWhile, tap } from 'rxjs/operators';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { ActionLoadFinish, ActionLoadStart } from '@core/interceptors/load.actions';
|
||||
import { NULL_UUID } from '@shared/models/id/has-uuid';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -135,7 +136,24 @@ export class EntitiesVersionControlService {
|
||||
}
|
||||
|
||||
public loadEntitiesVersion(request: VersionLoadRequest, config?: RequestConfig): Observable<VersionLoadResult> {
|
||||
return this.http.post<VersionLoadResult>('/api/entities/vc/entity', request, defaultHttpOptionsFromConfig(config));
|
||||
this.store.dispatch(new ActionLoadStart());
|
||||
return this.http.post<string>('/api/entities/vc/entity', request,
|
||||
defaultHttpOptionsFromConfig({...config, ...{ignoreLoading: true}})).pipe(
|
||||
switchMap((requestId) => {
|
||||
return timer(0, 2000).pipe(
|
||||
switchMap(() => this.getVersionLoadRequestStatus(requestId, config)),
|
||||
takeWhile((res) => !res.done, true),
|
||||
);
|
||||
}),
|
||||
finalize(() => {
|
||||
this.store.dispatch(new ActionLoadFinish());
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private getVersionLoadRequestStatus(requestId: string, config?: RequestConfig): Observable<VersionLoadResult> {
|
||||
return this.http.get<VersionLoadResult>(`/api/entities/vc/entity/${requestId}/status`,
|
||||
defaultHttpOptionsFromConfig({...config, ...{ignoreLoading: true}}));
|
||||
}
|
||||
|
||||
public compareEntityDataToVersion(branch: string,
|
||||
|
||||
@ -29,6 +29,7 @@ import { ActionLoadFinish, ActionLoadStart } from './load.actions';
|
||||
import { ActionNotificationShow } from '@app/core/notification/notification.actions';
|
||||
import { DialogService } from '@core/services/dialog.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { parseHttpErrorMessage } from '@core/utils';
|
||||
|
||||
let tmpHeaders = {};
|
||||
|
||||
@ -131,43 +132,12 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
|
||||
}
|
||||
|
||||
if (unhandled && !ignoreErrors) {
|
||||
let error = null;
|
||||
if (req.responseType === 'text') {
|
||||
try {
|
||||
error = errorResponse.error ? JSON.parse(errorResponse.error) : null;
|
||||
} catch (e) {}
|
||||
} else {
|
||||
error = errorResponse.error;
|
||||
}
|
||||
if (error && !error.message) {
|
||||
this.showError(this.prepareMessageFromData(error));
|
||||
} else if (error && error.message) {
|
||||
this.showError(error.message, error.timeout ? error.timeout : 0);
|
||||
} else {
|
||||
this.showError('Unhandled error code ' + (error ? error.status : '\'Unknown\''));
|
||||
}
|
||||
const errorMessageWithTimeout = parseHttpErrorMessage(errorResponse, this.translate, req.responseType);
|
||||
this.showError(errorMessageWithTimeout.message, errorMessageWithTimeout.timeout);
|
||||
}
|
||||
return throwError(errorResponse);
|
||||
}
|
||||
|
||||
private prepareMessageFromData(data) {
|
||||
if (typeof data === 'object' && data.constructor === ArrayBuffer) {
|
||||
const msg = String.fromCharCode.apply(null, new Uint8Array(data));
|
||||
try {
|
||||
const msgObj = JSON.parse(msg);
|
||||
if (msgObj.message) {
|
||||
return msgObj.message;
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
} catch (e) {
|
||||
return msg;
|
||||
}
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
private retryRequest(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
const thisTimeout = 1000 + Math.random() * 3000;
|
||||
return of(null).pipe(
|
||||
|
||||
@ -21,6 +21,10 @@ import { Datasource, DatasourceData, FormattedData, ReplaceInfo } from '@app/sha
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
import { NULL_UUID } from '@shared/models/id/has-uuid';
|
||||
import { EntityType, baseDetailsPageByEntityType } from '@shared/models/entity-type.models';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { letterSpacing } from 'html2canvas/dist/types/css/property-descriptors/letter-spacing';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { serverErrorCodesTranslations } from '@shared/models/constants';
|
||||
|
||||
const varsRegex = /\${([^}]*)}/g;
|
||||
|
||||
@ -664,3 +668,53 @@ export function randomAlphanumeric(length: number): string {
|
||||
export function getEntityDetailsPageURL(id: string, entityType: EntityType): string {
|
||||
return `${baseDetailsPageByEntityType.get(entityType)}/${id}`;
|
||||
}
|
||||
|
||||
export function parseHttpErrorMessage(errorResponse: HttpErrorResponse,
|
||||
translate: TranslateService, responseType?: string): {message: string, timeout: number} {
|
||||
let error = null;
|
||||
let errorMessage: string;
|
||||
let timeout = 0;
|
||||
if (responseType === 'text') {
|
||||
try {
|
||||
error = errorResponse.error ? JSON.parse(errorResponse.error) : null;
|
||||
} catch (e) {}
|
||||
} else {
|
||||
error = errorResponse.error;
|
||||
}
|
||||
if (error && !error.message) {
|
||||
errorMessage = prepareMessageFromData(error);
|
||||
} else if (error && error.message) {
|
||||
errorMessage = error.message;
|
||||
timeout = error.timeout ? error.timeout : 0;
|
||||
} else {
|
||||
errorMessage = `Unhandled error code ${error ? error.status : '\'Unknown\''}`;
|
||||
}
|
||||
if (isObject(errorMessage)) {
|
||||
let errorText = `${errorResponse.status}: `;
|
||||
let errorKey = null;
|
||||
if ((errorMessage as any).errorCode) {
|
||||
errorKey = serverErrorCodesTranslations.get((errorMessage as any).errorCode);
|
||||
}
|
||||
errorText += errorKey ? translate.instant(errorKey) : errorResponse.statusText;
|
||||
errorMessage = errorText;
|
||||
}
|
||||
return {message: errorMessage, timeout};
|
||||
}
|
||||
|
||||
function prepareMessageFromData(data): string {
|
||||
if (typeof data === 'object' && data.constructor === ArrayBuffer) {
|
||||
const msg = String.fromCharCode.apply(null, new Uint8Array(data));
|
||||
try {
|
||||
const msgObj = JSON.parse(msg);
|
||||
if (msgObj.message) {
|
||||
return msgObj.message;
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
} catch (e) {
|
||||
return msg;
|
||||
}
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
-->
|
||||
<section style="min-width: 800px;">
|
||||
<section *ngIf="!resultMessage">
|
||||
<section *ngIf="!versionCreateResult$">
|
||||
<mat-toolbar>
|
||||
<h2>{{ 'version-control.create-entities-version' | translate }}</h2>
|
||||
<span fxFlex></span>
|
||||
@ -69,9 +69,11 @@
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section *ngIf="resultMessage">
|
||||
<div class="mat-title vc-result-message" [innerHtml]="resultMessage"></div>
|
||||
<div fxLayoutAlign="end center" fxLayoutGap="8px">
|
||||
<section *ngIf="versionCreateResult$">
|
||||
<section *ngIf="resultMessage">
|
||||
<div class="mat-title vc-result-message" [innerHtml]="resultMessage"></div>
|
||||
</section>
|
||||
<div *ngIf="(versionCreateResult$ | async)?.done || hasError; else progress" fxLayoutAlign="end center" fxLayoutGap="8px">
|
||||
<button mat-button color="primary"
|
||||
type="button"
|
||||
[disabled]="(isLoading$ | async)"
|
||||
@ -79,5 +81,13 @@
|
||||
{{ 'action.close' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<ng-template #progress>
|
||||
<section fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="mat-title vc-result-message progress">
|
||||
<span translate>version-control.creating-version</span>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
</ng-template>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import {
|
||||
@ -30,13 +30,16 @@ import { EntitiesVersionControlService } from '@core/http/entities-version-contr
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { share } from 'rxjs/operators';
|
||||
import { parseHttpErrorMessage } from '@core/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-complex-version-create',
|
||||
templateUrl: './complex-version-create.component.html',
|
||||
styleUrls: ['./version-control.scss']
|
||||
})
|
||||
export class ComplexVersionCreateComponent extends PageComponent implements OnInit {
|
||||
export class ComplexVersionCreateComponent extends PageComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input()
|
||||
branch: string;
|
||||
@ -57,10 +60,16 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
|
||||
|
||||
resultMessage: SafeHtml;
|
||||
|
||||
hasError = false;
|
||||
|
||||
versionCreateResult: VersionCreationResult = null;
|
||||
|
||||
versionCreateBranch: string = null;
|
||||
|
||||
versionCreateResult$: Observable<VersionCreationResult>;
|
||||
|
||||
private versionCreateResultSubscription: Subscription;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private entitiesVersionControlService: EntitiesVersionControlService,
|
||||
private cd: ChangeDetectorRef,
|
||||
@ -79,6 +88,13 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
if (this.versionCreateResultSubscription) {
|
||||
this.versionCreateResultSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
if (this.onClose) {
|
||||
this.onClose(this.versionCreateResult, this.versionCreateBranch);
|
||||
@ -93,11 +109,20 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
|
||||
entityTypes: this.createVersionFormGroup.get('entityTypes').value,
|
||||
type: VersionCreateRequestType.COMPLEX
|
||||
};
|
||||
this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => {
|
||||
if (!result.added && !result.modified && !result.removed) {
|
||||
|
||||
this.versionCreateResult$ = this.entitiesVersionControlService.saveEntitiesVersion(request, {ignoreErrors: true}).pipe(
|
||||
share()
|
||||
);
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
|
||||
this.versionCreateResultSubscription = this.versionCreateResult$.subscribe((result) => {
|
||||
if (result.done && !result.added && !result.modified && !result.removed) {
|
||||
this.resultMessage = this.sanitizer.bypassSecurityTrustHtml(this.translate.instant('version-control.nothing-to-commit'));
|
||||
} else {
|
||||
this.resultMessage = this.sanitizer.bypassSecurityTrustHtml(this.translate.instant('version-control.version-create-result',
|
||||
this.resultMessage = this.sanitizer.bypassSecurityTrustHtml(result.error ? result.error : this.translate.instant('version-control.version-create-result',
|
||||
{added: result.added, modified: result.modified, removed: result.removed}));
|
||||
}
|
||||
this.versionCreateResult = result;
|
||||
@ -106,6 +131,14 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.hasError = true;
|
||||
this.resultMessage = this.sanitizer.bypassSecurityTrustHtml(parseHttpErrorMessage(error, this.translate).message);
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,8 @@
|
||||
limitations under the License.
|
||||
|
||||
-->
|
||||
<section [ngStyle]="versionLoadResult ? {minWidth: '500px'} : {minWidth: '800px'}">
|
||||
<section *ngIf="!versionLoadResult">
|
||||
<section [ngStyle]="versionLoadResult$ ? {minWidth: '500px'} : {minWidth: '800px'}">
|
||||
<section *ngIf="!versionLoadResult$">
|
||||
<mat-toolbar>
|
||||
<h2>{{ 'version-control.restore-entities-from-version' | translate: {versionName} }}</h2>
|
||||
<span fxFlex></span>
|
||||
@ -48,13 +48,13 @@
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section *ngIf="versionLoadResult">
|
||||
<div *ngIf="!entityTypeLoadResults.length && !errorMessage" class="mat-title vc-result-message">
|
||||
<section *ngIf="versionLoadResult$">
|
||||
<div *ngIf="(versionLoadResult$ | async)?.done && !entityTypeLoadResults?.length && !errorMessage" class="mat-title vc-result-message">
|
||||
{{ 'version-control.no-entities-restored' | translate }}
|
||||
</div>
|
||||
<div *ngIf="errorMessage" class="mat-title vc-result-message error" [innerHTML]="errorMessage"></div>
|
||||
<div *ngFor="let entityTypeLoadResult of entityTypeLoadResults" class="mat-title vc-result-message">{{ entityTypeLoadResultMessage(entityTypeLoadResult) }}</div>
|
||||
<div fxLayoutAlign="end center" fxLayoutGap="8px">
|
||||
<div *ngIf="(versionLoadResult$ | async)?.done || hasError; else progress" fxLayoutAlign="end center" fxLayoutGap="8px">
|
||||
<button mat-button color="primary"
|
||||
type="button"
|
||||
[disabled]="(isLoading$ | async)"
|
||||
@ -62,5 +62,13 @@
|
||||
{{ 'action.close' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<ng-template #progress>
|
||||
<section fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="mat-title vc-result-message progress">
|
||||
<span translate>version-control.restoring-entities-from-version</span>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
</ng-template>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import {
|
||||
@ -28,15 +28,18 @@ import { AppState } from '@core/core.state';
|
||||
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { entityTypeTranslations } from '@shared/models/entity-type.models';
|
||||
import { SafeHtml } from '@angular/platform-browser';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { share } from 'rxjs/operators';
|
||||
import { parseHttpErrorMessage } from '@core/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-complex-version-load',
|
||||
templateUrl: './complex-version-load.component.html',
|
||||
styleUrls: ['./version-control.scss']
|
||||
})
|
||||
export class ComplexVersionLoadComponent extends PageComponent implements OnInit {
|
||||
export class ComplexVersionLoadComponent extends PageComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input()
|
||||
branch: string;
|
||||
@ -61,10 +64,17 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit
|
||||
|
||||
errorMessage: SafeHtml;
|
||||
|
||||
hasError = false;
|
||||
|
||||
versionLoadResult$: Observable<VersionLoadResult>;
|
||||
|
||||
private versionLoadResultSubscription: Subscription;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private entitiesVersionControlService: EntitiesVersionControlService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private translate: TranslateService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private fb: FormBuilder) {
|
||||
super(store);
|
||||
}
|
||||
@ -75,6 +85,13 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
if (this.versionLoadResultSubscription) {
|
||||
this.versionLoadResultSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
entityTypeLoadResultMessage(result: EntityTypeLoadResult): string {
|
||||
const entityType = result.entityType;
|
||||
let message = this.translate.instant(entityTypeTranslations.get(entityType).typePlural) + ': ';
|
||||
@ -105,7 +122,14 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit
|
||||
entityTypes: this.loadVersionFormGroup.get('entityTypes').value,
|
||||
type: VersionLoadRequestType.ENTITY_TYPE
|
||||
};
|
||||
this.entitiesVersionControlService.loadEntitiesVersion(request).subscribe((result) => {
|
||||
this.versionLoadResult$ = this.entitiesVersionControlService.loadEntitiesVersion(request, {ignoreErrors: true}).pipe(
|
||||
share()
|
||||
);
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
this.versionLoadResultSubscription = this.versionLoadResult$.subscribe((result) => {
|
||||
this.versionLoadResult = result;
|
||||
this.entityTypeLoadResults = (result.result || []).filter(res => res.created || res.updated || res.deleted);
|
||||
if (result.error) {
|
||||
@ -115,6 +139,14 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.hasError = true;
|
||||
this.errorMessage = this.sanitizer.bypassSecurityTrustHtml(parseHttpErrorMessage(error, this.translate).message);
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
</div>
|
||||
</section>
|
||||
<section *ngIf="versionCreateResult$">
|
||||
<section *ngIf="(versionCreateResult$ | async)?.done; else progress">
|
||||
<section *ngIf="(versionCreateResult$ | async)?.done || resultMessage; else progress">
|
||||
<section *ngIf="resultMessage">
|
||||
<div class="mat-title vc-result-message">{{ resultMessage }}</div>
|
||||
<div fxLayoutAlign="end center" fxLayoutGap="8px">
|
||||
|
||||
@ -31,6 +31,7 @@ import { Observable, of, Subscription } from 'rxjs';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||
import { share } from 'rxjs/operators';
|
||||
import { parseHttpErrorMessage } from '@core/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-entity-version-create',
|
||||
@ -113,7 +114,7 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
|
||||
},
|
||||
type: VersionCreateRequestType.SINGLE_ENTITY
|
||||
};
|
||||
this.versionCreateResult$ = this.entitiesVersionControlService.saveEntitiesVersion(request).pipe(
|
||||
this.versionCreateResult$ = this.entitiesVersionControlService.saveEntitiesVersion(request, {ignoreErrors: true}).pipe(
|
||||
share()
|
||||
);
|
||||
this.cd.detectChanges();
|
||||
@ -133,6 +134,13 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
|
||||
this.onClose(result, request.branch);
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.resultMessage = parseHttpErrorMessage(error, this.translate).message;
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
-->
|
||||
<section [ngStyle]="entityDataInfo ? {minWidth: '400px'} : {}">
|
||||
<section *ngIf="!errorMessage">
|
||||
<section *ngIf="!versionLoadResult$">
|
||||
<mat-toolbar *ngIf="entityDataInfo">
|
||||
<h2>{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}</h2>
|
||||
<span fxFlex></span>
|
||||
@ -55,15 +55,27 @@
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<section *ngIf="errorMessage">
|
||||
<div class="mat-title vc-result-message error" [innerHTML]="errorMessage"></div>
|
||||
<div fxLayoutAlign="end center" fxLayoutGap="8px">
|
||||
<button mat-button color="primary"
|
||||
type="button"
|
||||
[disabled]="(isLoading$ | async)"
|
||||
(click)="cancel()" cdkFocusInitial>
|
||||
{{ 'action.close' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<section *ngIf="versionLoadResult$">
|
||||
<section *ngIf="(versionLoadResult$ | async)?.done || errorMessage; else progress">
|
||||
<section *ngIf="errorMessage">
|
||||
<div class="mat-title vc-result-message error" [innerHTML]="errorMessage"></div>
|
||||
<div fxLayoutAlign="end center" fxLayoutGap="8px">
|
||||
<button mat-button color="primary"
|
||||
type="button"
|
||||
[disabled]="(isLoading$ | async)"
|
||||
(click)="cancel()" cdkFocusInitial>
|
||||
{{ 'action.close' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
<ng-template #progress>
|
||||
<section fxLayout="column" fxLayoutAlign="center center">
|
||||
<div class="mat-title vc-result-message progress">
|
||||
<span translate>version-control.restoring-entity-version</span>
|
||||
<mat-progress-bar mode="indeterminate"></mat-progress-bar>
|
||||
</div>
|
||||
</section>
|
||||
</ng-template>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@ -14,12 +14,12 @@
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit, Sanitizer } from '@angular/core';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
||||
import {
|
||||
EntityDataInfo,
|
||||
SingleEntityVersionLoadRequest,
|
||||
SingleEntityVersionLoadRequest, VersionCreationResult,
|
||||
VersionLoadRequestType,
|
||||
VersionLoadResult
|
||||
} from '@shared/models/vc.models';
|
||||
@ -29,15 +29,17 @@ import { EntitiesVersionControlService } from '@core/http/entities-version-contr
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { TbPopoverComponent } from '@shared/components/popover.component';
|
||||
import { delay } from 'rxjs/operators';
|
||||
import { SafeHtml } from '@angular/platform-browser';
|
||||
import { delay, share } from 'rxjs/operators';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { parseHttpErrorMessage } from '@core/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-entity-version-restore',
|
||||
templateUrl: './entity-version-restore.component.html',
|
||||
styleUrls: ['./version-control.scss']
|
||||
})
|
||||
export class EntityVersionRestoreComponent extends PageComponent implements OnInit {
|
||||
export class EntityVersionRestoreComponent extends PageComponent implements OnInit, OnDestroy {
|
||||
|
||||
@Input()
|
||||
branch: string;
|
||||
@ -63,10 +65,15 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
|
||||
|
||||
errorMessage: SafeHtml;
|
||||
|
||||
versionLoadResult$: Observable<VersionLoadResult>;
|
||||
|
||||
private versionLoadResultSubscription: Subscription;
|
||||
|
||||
constructor(protected store: Store<AppState>,
|
||||
private entitiesVersionControlService: EntitiesVersionControlService,
|
||||
private cd: ChangeDetectorRef,
|
||||
private translate: TranslateService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private fb: FormBuilder) {
|
||||
super(store);
|
||||
}
|
||||
@ -86,6 +93,13 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
super.ngOnDestroy();
|
||||
if (this.versionLoadResultSubscription) {
|
||||
this.versionLoadResultSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
if (this.onClose) {
|
||||
this.onClose(null);
|
||||
@ -104,18 +118,34 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
|
||||
},
|
||||
type: VersionLoadRequestType.SINGLE_ENTITY
|
||||
};
|
||||
this.entitiesVersionControlService.loadEntitiesVersion(request).subscribe((result) => {
|
||||
if (result.error) {
|
||||
this.errorMessage = this.entitiesVersionControlService.entityLoadErrorToMessage(result.error);
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
} else {
|
||||
if (this.onClose) {
|
||||
this.onClose(result);
|
||||
this.versionLoadResult$ = this.entitiesVersionControlService.loadEntitiesVersion(request, {ignoreErrors: true}).pipe(
|
||||
share()
|
||||
);
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
this.versionLoadResultSubscription = this.versionLoadResult$.subscribe((result) => {
|
||||
if (result.done) {
|
||||
if (result.error) {
|
||||
this.errorMessage = this.entitiesVersionControlService.entityLoadErrorToMessage(result.error);
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
} else {
|
||||
if (this.onClose) {
|
||||
this.onClose(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
this.errorMessage = this.sanitizer.bypassSecurityTrustHtml(parseHttpErrorMessage(error, this.translate).message);
|
||||
this.cd.detectChanges();
|
||||
if (this.popoverComponent) {
|
||||
this.popoverComponent.updatePosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ import {
|
||||
import cssjs from '@core/css/css';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { hashCode, isDefined, isNumber } from '@core/utils';
|
||||
import { hashCode, isDefined, isNumber, parseHttpErrorMessage } from '@core/utils';
|
||||
import { CollectionViewer, DataSource } from '@angular/cdk/collections';
|
||||
import { emptyPageData, PageData } from '@shared/models/page/page-data';
|
||||
import {
|
||||
@ -520,7 +520,7 @@ class PersistentDatasource implements DataSource<PersistentRpcData> {
|
||||
this.subscription.rpcErrorText = 'Request Timeout.';
|
||||
} else {
|
||||
this.subscription.rpcErrorText = 'Error : ' + rejection.status + ' - ' + rejection.statusText;
|
||||
const error = this.extractRejectionErrorText(rejection);
|
||||
const error = parseHttpErrorMessage(rejection, this.translate);
|
||||
if (error) {
|
||||
this.subscription.rpcErrorText += '</br>';
|
||||
this.subscription.rpcErrorText += error.message || '';
|
||||
@ -534,40 +534,6 @@ class PersistentDatasource implements DataSource<PersistentRpcData> {
|
||||
return rpcSubject.asObservable();
|
||||
}
|
||||
|
||||
extractRejectionErrorText(rejection: HttpErrorResponse) {
|
||||
let error = null;
|
||||
if (rejection.error) {
|
||||
error = rejection.error;
|
||||
try {
|
||||
error = rejection.error ? JSON.parse(rejection.error) : null;
|
||||
} catch (e) {}
|
||||
}
|
||||
if (error && !error.message) {
|
||||
error = this.prepareMessageFromData(error);
|
||||
} else if (error && error.message) {
|
||||
error = error.message;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
prepareMessageFromData(data) {
|
||||
if (typeof data === 'object' && data.constructor === ArrayBuffer) {
|
||||
const msg = String.fromCharCode.apply(null, new Uint8Array(data));
|
||||
try {
|
||||
const msgObj = JSON.parse(msg);
|
||||
if (msgObj.message) {
|
||||
return msgObj.message;
|
||||
} else {
|
||||
return msg;
|
||||
}
|
||||
} catch (e) {
|
||||
return msg;
|
||||
}
|
||||
} else {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
isEmpty(): Observable<boolean> {
|
||||
return this.persistentSubject.pipe(
|
||||
map((requests) => !requests.length)
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import { IModulesMap } from '@modules/common/modules-map.models';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
|
||||
export const Constants = {
|
||||
serverErrorCode: {
|
||||
@ -38,6 +39,20 @@ export const Constants = {
|
||||
}
|
||||
};
|
||||
|
||||
export const serverErrorCodesTranslations = new Map<number, string>([
|
||||
[Constants.serverErrorCode.general, 'server-error.general'],
|
||||
[Constants.serverErrorCode.authentication, 'server-error.authentication'],
|
||||
[Constants.serverErrorCode.jwtTokenExpired, 'server-error.jwt-token-expired'],
|
||||
[Constants.serverErrorCode.tenantTrialExpired, 'server-error.tenant-trial-expired'],
|
||||
[Constants.serverErrorCode.credentialsExpired, 'server-error.credentials-expired'],
|
||||
[Constants.serverErrorCode.permissionDenied, 'server-error.permission-denied'],
|
||||
[Constants.serverErrorCode.invalidArguments, 'server-error.invalid-arguments'],
|
||||
[Constants.serverErrorCode.badRequestParams, 'server-error.bad-request-params'],
|
||||
[Constants.serverErrorCode.itemNotFound, 'server-error.item-not-found'],
|
||||
[Constants.serverErrorCode.tooManyRequests, 'server-error.too-many-requests'],
|
||||
[Constants.serverErrorCode.tooManyUpdates, 'server-error.too-many-updates'],
|
||||
]);
|
||||
|
||||
export const MediaBreakpoints = {
|
||||
xs: 'screen and (max-width: 599px)',
|
||||
sm: 'screen and (min-width: 600px) and (max-width: 959px)',
|
||||
|
||||
@ -197,6 +197,7 @@ export interface EntityLoadError {
|
||||
export interface VersionLoadResult {
|
||||
result: Array<EntityTypeLoadResult>;
|
||||
error: EntityLoadError;
|
||||
done: boolean;
|
||||
}
|
||||
|
||||
export interface AttributeExportData {
|
||||
|
||||
@ -3000,6 +3000,19 @@
|
||||
"retry-failed-and-timeout-hint": "Retry all failed and timed-out messages from processing pack"
|
||||
}
|
||||
},
|
||||
"server-error": {
|
||||
"general": "General server error",
|
||||
"authentication": "Authentication error",
|
||||
"jwt-token-expired": "JWT token expired",
|
||||
"tenant-trial-expired": "Tenant trial expired",
|
||||
"credentials-expired": "Credentials expired",
|
||||
"permission-denied": "Permission denied",
|
||||
"invalid-arguments": "Invalid arguments",
|
||||
"bad-request-params": "Bad request params",
|
||||
"item-not-found": "Item not found",
|
||||
"too-many-requests": "Too many requests",
|
||||
"too-many-updates": "Too many updates"
|
||||
},
|
||||
"tenant": {
|
||||
"tenant": "Tenant",
|
||||
"tenants": "Tenants",
|
||||
@ -3310,6 +3323,7 @@
|
||||
"nothing-to-commit": "No changes to commit",
|
||||
"restore-version": "Restore version",
|
||||
"restore-entity-from-version": "Restore entity from version '{{versionName}}'",
|
||||
"restoring-entity-version": "Restoring entity version... Please wait",
|
||||
"load-relations": "Load relations",
|
||||
"load-attributes": "Load attributes",
|
||||
"load-credentials": "Load credentials",
|
||||
@ -3335,6 +3349,7 @@
|
||||
"remove-other-entities": "Remove other entities",
|
||||
"find-existing-entity-by-name": "Find existing entity by name",
|
||||
"restore-entities-from-version": "Restore entities from version '{{versionName}}'",
|
||||
"restoring-entities-from-version": "Restoring entities... Please wait",
|
||||
"no-entities-restored": "No entities restored",
|
||||
"created": "{{created}} created",
|
||||
"updated": "{{updated}} updated",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user