diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index 635e98d89c..37cc0c5584 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -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 += '
'; - 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); diff --git a/ui-ngx/src/app/core/http/entities-version-control.service.ts b/ui-ngx/src/app/core/http/entities-version-control.service.ts index bded799c4b..ec0b6ba2ae 100644 --- a/ui-ngx/src/app/core/http/entities-version-control.service.ts +++ b/ui-ngx/src/app/core/http/entities-version-control.service.ts @@ -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 { - return this.http.post('/api/entities/vc/entity', request, defaultHttpOptionsFromConfig(config)); + this.store.dispatch(new ActionLoadStart()); + return this.http.post('/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 { + return this.http.get(`/api/entities/vc/entity/${requestId}/status`, + defaultHttpOptionsFromConfig({...config, ...{ignoreLoading: true}})); } public compareEntityDataToVersion(branch: string, diff --git a/ui-ngx/src/app/core/interceptors/global-http-interceptor.ts b/ui-ngx/src/app/core/interceptors/global-http-interceptor.ts index 94968f393a..15c4d3ede3 100644 --- a/ui-ngx/src/app/core/interceptors/global-http-interceptor.ts +++ b/ui-ngx/src/app/core/interceptors/global-http-interceptor.ts @@ -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, next: HttpHandler): Observable> { const thisTimeout = 1000 + Math.random() * 3000; return of(null).pipe( diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 37d2f1f652..bc1af5fa6f 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -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; + } +} diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html b/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html index 9e15076788..3e849773c3 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html @@ -16,7 +16,7 @@ -->
-
+

{{ 'version-control.create-entities-version' | translate }}

@@ -69,9 +69,11 @@
-
-
-
+
+
+
+
+
+ +
+
+ version-control.creating-version + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.ts b/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.ts index bb97bfaedf..5348b4b340 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.ts @@ -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; + + private versionCreateResultSubscription: Subscription; + constructor(protected store: Store, 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(); + } }); } } diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html index e52be641ce..05f16b3e2a 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html @@ -15,8 +15,8 @@ limitations under the License. --> -
-
+
+

{{ 'version-control.restore-entities-from-version' | translate: {versionName} }}

@@ -48,13 +48,13 @@
-
-
+
+
{{ 'version-control.no-entities-restored' | translate }}
{{ entityTypeLoadResultMessage(entityTypeLoadResult) }}
-
+
+ +
+
+ version-control.restoring-entities-from-version + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts index a212b7b2ce..718dc5c2e7 100644 --- a/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts @@ -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; + + private versionLoadResultSubscription: Subscription; + constructor(protected store: Store, 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(); + } }); } } diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html index 157dcf56a1..e9c6122a46 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html @@ -66,7 +66,7 @@
-
+
{{ resultMessage }}
diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts index 469ff49a0c..6706d80c71 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts @@ -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(); + } }); }); } diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html index fa2d1f28ae..b096c1907f 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html @@ -16,7 +16,7 @@ -->
-
+

{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}

@@ -55,15 +55,27 @@
-
-
-
- -
+
+
+
+
+
+ +
+
+
+ +
+
+ version-control.restoring-entity-version + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts index c31030f74b..43d2452ea3 100644 --- a/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts +++ b/ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts @@ -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; + + private versionLoadResultSubscription: Subscription; + constructor(protected store: Store, 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(); + } }); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts index 5beef7ea8c..b9b294cccd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/rpc/persistent-table.component.ts @@ -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 { 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 += '
'; this.subscription.rpcErrorText += error.message || ''; @@ -534,40 +534,6 @@ class PersistentDatasource implements DataSource { 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 { return this.persistentSubject.pipe( map((requests) => !requests.length) diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index b746ee71b0..28c08f09a0 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -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([ + [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)', diff --git a/ui-ngx/src/app/shared/models/vc.models.ts b/ui-ngx/src/app/shared/models/vc.models.ts index 15b75c4c10..98f66f74a9 100644 --- a/ui-ngx/src/app/shared/models/vc.models.ts +++ b/ui-ngx/src/app/shared/models/vc.models.ts @@ -197,6 +197,7 @@ export interface EntityLoadError { export interface VersionLoadResult { result: Array; error: EntityLoadError; + done: boolean; } export interface AttributeExportData { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index f361574e63..1207d0763e 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -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",