Browse Source

UI: Version load error handling

pull/6641/head
Igor Kulikov 4 years ago
parent
commit
57a9a0a6d3
  1. 32
      ui-ngx/src/app/core/http/entities-version-control.service.ts
  2. 3
      ui-ngx/src/app/modules/home/components/vc/auto-commit-settings.component.html
  3. 8
      ui-ngx/src/app/modules/home/components/vc/auto-commit-settings.component.ts
  4. 2
      ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html
  5. 21
      ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.scss
  6. 23
      ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.ts
  7. 11
      ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html
  8. 34
      ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts
  9. 5
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html
  10. 4
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts
  11. 5
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html
  12. 4
      ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts
  13. 5
      ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html
  14. 21
      ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.scss
  15. 22
      ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts
  16. 4
      ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts
  17. 84
      ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html
  18. 25
      ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts
  19. 4
      ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html
  20. 7
      ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.scss
  21. 24
      ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts
  22. 2
      ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html
  23. 7
      ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts
  24. 3
      ui-ngx/src/app/modules/home/components/vc/version-control.component.html
  25. 3
      ui-ngx/src/app/modules/home/components/vc/version-control.component.ts
  26. 6
      ui-ngx/src/app/modules/home/components/vc/version-control.scss
  27. 4
      ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts
  28. 30
      ui-ngx/src/app/shared/models/vc.models.ts
  29. 8
      ui-ngx/src/assets/locale/locale.constant-en_US.json

32
ui-ngx/src/app/core/http/entities-version-control.service.ts

@ -19,7 +19,7 @@ import { HttpClient } from '@angular/common/http';
import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils'; import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { import {
BranchInfo, EntityDataDiff, EntityDataInfo, BranchInfo, EntityDataDiff, EntityDataInfo, EntityLoadError, entityLoadErrorTranslationMap, EntityLoadErrorType,
EntityVersion, EntityVersion,
VersionCreateRequest, VersionCreateRequest,
VersionCreationResult, VersionCreationResult,
@ -28,11 +28,13 @@ import {
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { EntityType } from '@shared/models/entity-type.models'; import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { selectIsUserLoaded } from '@core/auth/auth.selectors'; import { selectIsUserLoaded } from '@core/auth/auth.selectors';
import { catchError, tap } from 'rxjs/operators'; import { catchError, tap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -43,6 +45,8 @@ export class EntitiesVersionControlService {
constructor( constructor(
private http: HttpClient, private http: HttpClient,
private translate: TranslateService,
private sanitizer: DomSanitizer,
private store: Store<AppState> private store: Store<AppState>
) { ) {
@ -109,8 +113,8 @@ export class EntitiesVersionControlService {
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }
public loadEntitiesVersion(request: VersionLoadRequest, config?: RequestConfig): Observable<Array<VersionLoadResult>> { public loadEntitiesVersion(request: VersionLoadRequest, config?: RequestConfig): Observable<VersionLoadResult> {
return this.http.post<Array<VersionLoadResult>>('/api/entities/vc/entity', request, defaultHttpOptionsFromConfig(config)); return this.http.post<VersionLoadResult>('/api/entities/vc/entity', request, defaultHttpOptionsFromConfig(config));
} }
public compareEntityDataToVersion(branch: string, public compareEntityDataToVersion(branch: string,
@ -120,4 +124,24 @@ export class EntitiesVersionControlService {
return this.http.get<EntityDataDiff>(`/api/entities/vc/diff/${branch}/${entityId.entityType}/${entityId.id}?versionId=${versionId}`, return this.http.get<EntityDataDiff>(`/api/entities/vc/diff/${branch}/${entityId.entityType}/${entityId.id}?versionId=${versionId}`,
defaultHttpOptionsFromConfig(config)); defaultHttpOptionsFromConfig(config));
} }
public entityLoadErrorToMessage(entityLoadError: EntityLoadError): SafeHtml {
const type = entityLoadError.type;
const messageId = entityLoadErrorTranslationMap.get(type);
const messageArgs = {} as any;
switch (type) {
case EntityLoadErrorType.DEVICE_CREDENTIALS_CONFLICT:
messageArgs.entityId = entityLoadError.source.id;
break;
case EntityLoadErrorType.MISSING_REFERENCED_ENTITY:
messageArgs.sourceEntityTypeName =
(this.translate.instant(entityTypeTranslations.get(entityLoadError.source.entityType).type) as string).toLowerCase();
messageArgs.sourceEntityId = entityLoadError.source.id;
messageArgs.targetEntityTypeName =
(this.translate.instant(entityTypeTranslations.get(entityLoadError.target.entityType).type) as string).toLowerCase();
messageArgs.targetEntityId = entityLoadError.target.id;
break;
}
return this.sanitizer.bypassSecurityTrustHtml(this.translate.instant(messageId, messageArgs));
}
} }

3
ui-ngx/src/app/modules/home/components/vc/auto-commit-settings.component.html

@ -75,6 +75,9 @@
</div> </div>
</div> </div>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px" formGroupName="config"> <div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px" formGroupName="config">
<mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="saveCredentials">
{{ 'version-control.export-credentials' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="saveAttributes"> <mat-checkbox formControlName="saveAttributes">
{{ 'version-control.export-attributes' | translate }} {{ 'version-control.export-attributes' | translate }}
</mat-checkbox> </mat-checkbox>

8
ui-ngx/src/app/modules/home/components/vc/auto-commit-settings.component.ts

@ -39,6 +39,8 @@ export class AutoCommitSettingsComponent extends PageComponent implements OnInit
autoCommitSettingsForm: FormGroup; autoCommitSettingsForm: FormGroup;
settings: AutoCommitSettings = null; settings: AutoCommitSettings = null;
entityTypes = EntityType;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private adminService: AdminService, private adminService: AdminService,
private dialogService: DialogService, private dialogService: DialogService,
@ -98,7 +100,8 @@ export class AutoCommitSettingsComponent extends PageComponent implements OnInit
const config: AutoVersionCreateConfig = { const config: AutoVersionCreateConfig = {
branch: null, branch: null,
saveAttributes: true, saveAttributes: true,
saveRelations: false saveRelations: false,
saveCredentials: true
}; };
const allowed = this.allowedEntityTypes(); const allowed = this.allowedEntityTypes();
let entityType: EntityType = null; let entityType: EntityType = null;
@ -199,7 +202,8 @@ export class AutoCommitSettingsComponent extends PageComponent implements OnInit
config: this.fb.group({ config: this.fb.group({
branch: [config.branch, []], branch: [config.branch, []],
saveRelations: [config.saveRelations, []], saveRelations: [config.saveRelations, []],
saveAttributes: [config.saveAttributes, []] saveAttributes: [config.saveAttributes, []],
saveCredentials: [config.saveCredentials, []]
}) })
} }
); );

2
ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.html

@ -70,7 +70,7 @@
</div> </div>
</section> </section>
<section *ngIf="resultMessage"> <section *ngIf="resultMessage">
<div class="mat-title create-result-message">{{ resultMessage }}</div> <div class="mat-title vc-result-message" [innerHtml]="resultMessage"></div>
<div fxLayoutAlign="end center" fxLayoutGap="8px"> <div fxLayoutAlign="end center" fxLayoutGap="8px">
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"

21
ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.scss

@ -1,21 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed 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.
*/
:host {
.create-result-message {
padding: 48px 8px 8px;
text-align: center;
}
}

23
ui-ngx/src/app/modules/home/components/vc/complex-version-create.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { import {
@ -28,11 +28,13 @@ import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state'; import { AppState } from '@core/core.state';
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service'; import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TbPopoverComponent } from '@shared/components/popover.component';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
@Component({ @Component({
selector: 'tb-complex-version-create', selector: 'tb-complex-version-create',
templateUrl: './complex-version-create.component.html', templateUrl: './complex-version-create.component.html',
styleUrls: ['./complex-version-create.component.scss'] styleUrls: ['./version-control.scss']
}) })
export class ComplexVersionCreateComponent extends PageComponent implements OnInit { export class ComplexVersionCreateComponent extends PageComponent implements OnInit {
@ -43,7 +45,7 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
onClose: (result: VersionCreationResult | null, branch: string | null) => void; onClose: (result: VersionCreationResult | null, branch: string | null) => void;
@Input() @Input()
onContentUpdated: () => void; popoverComponent: TbPopoverComponent;
createVersionFormGroup: FormGroup; createVersionFormGroup: FormGroup;
@ -53,7 +55,7 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
syncStrategyHints = syncStrategyHintMap; syncStrategyHints = syncStrategyHintMap;
resultMessage: string; resultMessage: SafeHtml;
versionCreateResult: VersionCreationResult = null; versionCreateResult: VersionCreationResult = null;
@ -61,6 +63,8 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private entitiesVersionControlService: EntitiesVersionControlService, private entitiesVersionControlService: EntitiesVersionControlService,
private cd: ChangeDetectorRef,
private sanitizer: DomSanitizer,
private translate: TranslateService, private translate: TranslateService,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store); super(store);
@ -91,15 +95,16 @@ export class ComplexVersionCreateComponent extends PageComponent implements OnIn
}; };
this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => { this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => {
if (!result.added && !result.modified && !result.removed) { if (!result.added && !result.modified && !result.removed) {
this.resultMessage = this.translate.instant('version-control.nothing-to-commit'); this.resultMessage = this.sanitizer.bypassSecurityTrustHtml(this.translate.instant('version-control.nothing-to-commit'));
} else { } else {
this.resultMessage = this.translate.instant('version-control.version-create-result', this.resultMessage = this.sanitizer.bypassSecurityTrustHtml(this.translate.instant('version-control.version-create-result',
{added: result.added, modified: result.modified, removed: result.removed}); {added: result.added, modified: result.modified, removed: result.removed}));
} }
this.versionCreateResult = result; this.versionCreateResult = result;
this.versionCreateBranch = request.branch; this.versionCreateBranch = request.branch;
if (this.onContentUpdated) { this.cd.detectChanges();
this.onContentUpdated(); if (this.popoverComponent) {
this.popoverComponent.updatePosition();
} }
}); });
} }

11
ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<section [ngStyle]="versionLoadResults ? {minWidth: '500px'} : {minWidth: '800px'}"> <section [ngStyle]="versionLoadResult ? {minWidth: '500px'} : {minWidth: '800px'}">
<section *ngIf="!versionLoadResults"> <section *ngIf="!versionLoadResult">
<mat-toolbar> <mat-toolbar>
<h2>{{ 'version-control.restore-entities-from-version' | translate: {versionName} }}</h2> <h2>{{ 'version-control.restore-entities-from-version' | translate: {versionName} }}</h2>
<span fxFlex></span> <span fxFlex></span>
@ -48,11 +48,12 @@
</button> </button>
</div> </div>
</section> </section>
<section *ngIf="versionLoadResults"> <section *ngIf="versionLoadResult">
<div *ngIf="!versionLoadResults.length" class="mat-title load-result-message"> <div *ngIf="!entityTypeLoadResults.length && !errorMessage" class="mat-title vc-result-message">
{{ 'version-control.no-entities-restored' | translate }} {{ 'version-control.no-entities-restored' | translate }}
</div> </div>
<div *ngFor="let versionLoadResult of versionLoadResults" class="mat-title load-result-message">{{ versionLoadResultMessage(versionLoadResult) }}</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 fxLayoutAlign="end center" fxLayoutGap="8px">
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"

34
ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.ts

@ -14,11 +14,11 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { FormBuilder, FormGroup } from '@angular/forms'; import { FormBuilder, FormGroup } from '@angular/forms';
import { import {
createDefaultEntityTypesVersionLoad, createDefaultEntityTypesVersionLoad, EntityTypeLoadResult,
EntityTypeVersionLoadRequest, EntityTypeVersionLoadRequest,
VersionLoadRequestType, VersionLoadRequestType,
VersionLoadResult VersionLoadResult
@ -28,11 +28,13 @@ import { AppState } from '@core/core.state';
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service'; import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { entityTypeTranslations } from '@shared/models/entity-type.models'; import { entityTypeTranslations } from '@shared/models/entity-type.models';
import { SafeHtml } from '@angular/platform-browser';
import { TbPopoverComponent } from '@shared/components/popover.component';
@Component({ @Component({
selector: 'tb-complex-version-load', selector: 'tb-complex-version-load',
templateUrl: './complex-version-load.component.html', templateUrl: './complex-version-load.component.html',
styleUrls: ['./complex-version-load.component.scss'] styleUrls: ['./version-control.scss']
}) })
export class ComplexVersionLoadComponent extends PageComponent implements OnInit { export class ComplexVersionLoadComponent extends PageComponent implements OnInit {
@ -46,17 +48,22 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit
versionId: string; versionId: string;
@Input() @Input()
onClose: (result: Array<VersionLoadResult> | null) => void; onClose: (result: VersionLoadResult | null) => void;
@Input() @Input()
onContentUpdated: () => void; popoverComponent: TbPopoverComponent;
loadVersionFormGroup: FormGroup; loadVersionFormGroup: FormGroup;
versionLoadResults: Array<VersionLoadResult> = null; versionLoadResult: VersionLoadResult = null;
entityTypeLoadResults: Array<EntityTypeLoadResult> = null;
errorMessage: SafeHtml;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private entitiesVersionControlService: EntitiesVersionControlService, private entitiesVersionControlService: EntitiesVersionControlService,
private cd: ChangeDetectorRef,
private translate: TranslateService, private translate: TranslateService,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store); super(store);
@ -68,7 +75,7 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit
}); });
} }
versionLoadResultMessage(result: VersionLoadResult): string { entityTypeLoadResultMessage(result: EntityTypeLoadResult): string {
const entityType = result.entityType; const entityType = result.entityType;
let message = this.translate.instant(entityTypeTranslations.get(entityType).typePlural) + ': '; let message = this.translate.instant(entityTypeTranslations.get(entityType).typePlural) + ': ';
const resultMessages: string[] = []; const resultMessages: string[] = [];
@ -87,7 +94,7 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit
cancel(): void { cancel(): void {
if (this.onClose) { if (this.onClose) {
this.onClose(this.versionLoadResults); this.onClose(this.versionLoadResult);
} }
} }
@ -99,9 +106,14 @@ export class ComplexVersionLoadComponent extends PageComponent implements OnInit
type: VersionLoadRequestType.ENTITY_TYPE type: VersionLoadRequestType.ENTITY_TYPE
}; };
this.entitiesVersionControlService.loadEntitiesVersion(request).subscribe((result) => { this.entitiesVersionControlService.loadEntitiesVersion(request).subscribe((result) => {
this.versionLoadResults = (result || []).filter(res => res.created || res.updated || res.deleted); this.versionLoadResult = result;
if (this.onContentUpdated) { this.entityTypeLoadResults = (result.result || []).filter(res => res.created || res.updated || res.deleted);
this.onContentUpdated(); if (result.error) {
this.errorMessage = this.entitiesVersionControlService.entityLoadErrorToMessage(result.error);
}
this.cd.detectChanges();
if (this.popoverComponent) {
this.popoverComponent.updatePosition();
} }
}); });
} }

5
ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.html

@ -52,7 +52,7 @@
[filterAllowedEntityTypes]="false" [filterAllowedEntityTypes]="false"
[allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)"> [allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)">
</tb-entity-type-select> </tb-entity-type-select>
<div fxFlex fxLayout="row" fxLayoutGap="16px" fxLayoutAlign="start center" formGroupName="config"> <div fxFlex fxLayout="row" fxLayoutGap="16px" formGroupName="config">
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>version-control.sync-strategy</mat-label> <mat-label translate>version-control.sync-strategy</mat-label>
<mat-select formControlName="syncStrategy"> <mat-select formControlName="syncStrategy">
@ -65,6 +65,9 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<div fxFlex fxLayout="column" fxLayoutGap="8px"> <div fxFlex fxLayout="column" fxLayoutGap="8px">
<mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="saveCredentials">
{{ 'version-control.export-credentials' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="saveAttributes"> <mat-checkbox formControlName="saveAttributes">
{{ 'version-control.export-attributes' | translate }} {{ 'version-control.export-attributes' | translate }}
</mat-checkbox> </mat-checkbox>

4
ui-ngx/src/app/modules/home/components/vc/entity-types-version-create.component.ts

@ -72,6 +72,8 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements
syncStrategyTranslations = syncStrategyTranslationMap; syncStrategyTranslations = syncStrategyTranslationMap;
entityTypes = EntityType;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private fb: FormBuilder) { private fb: FormBuilder) {
@ -136,6 +138,7 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements
syncStrategy: [config.syncStrategy === null ? 'default' : config.syncStrategy, []], syncStrategy: [config.syncStrategy === null ? 'default' : config.syncStrategy, []],
saveRelations: [config.saveRelations, []], saveRelations: [config.saveRelations, []],
saveAttributes: [config.saveAttributes, []], saveAttributes: [config.saveAttributes, []],
saveCredentials: [config.saveCredentials, []],
allEntities: [config.allEntities, []], allEntities: [config.allEntities, []],
entityIds: [config.entityIds, [Validators.required]] entityIds: [config.entityIds, [Validators.required]]
}) })
@ -185,6 +188,7 @@ export class EntityTypesVersionCreateComponent extends PageComponent implements
syncStrategy: null, syncStrategy: null,
saveAttributes: true, saveAttributes: true,
saveRelations: true, saveRelations: true,
saveCredentials: true,
allEntities: true, allEntities: true,
entityIds: [] entityIds: []
}; };

5
ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.html

@ -52,7 +52,7 @@
[filterAllowedEntityTypes]="false" [filterAllowedEntityTypes]="false"
[allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)"> [allowedEntityTypes]="allowedEntityTypes(entityTypeFormGroup)">
</tb-entity-type-select> </tb-entity-type-select>
<div fxFlex fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="16px" formGroupName="config"> <div fxFlex fxLayout="row" fxLayoutGap="16px" formGroupName="config">
<div fxFlex fxLayout="column" fxLayoutGap="8px"> <div fxFlex fxLayout="column" fxLayoutGap="8px">
<mat-checkbox #removeOtherEntitiesCheckbox <mat-checkbox #removeOtherEntitiesCheckbox
formControlName="removeOtherEntities" formControlName="removeOtherEntities"
@ -64,6 +64,9 @@
</mat-checkbox> </mat-checkbox>
</div> </div>
<div fxFlex fxLayout="column" fxLayoutGap="8px"> <div fxFlex fxLayout="column" fxLayoutGap="8px">
<mat-checkbox *ngIf="entityTypeFormGroup.get('entityType').value === entityTypes.DEVICE" formControlName="loadCredentials">
{{ 'version-control.load-credentials' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="loadAttributes"> <mat-checkbox formControlName="loadAttributes">
{{ 'version-control.load-attributes' | translate }} {{ 'version-control.load-attributes' | translate }}
</mat-checkbox> </mat-checkbox>

4
ui-ngx/src/app/modules/home/components/vc/entity-types-version-load.component.ts

@ -66,6 +66,8 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
public entityTypesVersionLoadFormGroup: FormGroup; public entityTypesVersionLoadFormGroup: FormGroup;
entityTypes = EntityType;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private translate: TranslateService, private translate: TranslateService,
private popoverService: TbPopoverService, private popoverService: TbPopoverService,
@ -132,6 +134,7 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
config: this.fb.group({ config: this.fb.group({
loadRelations: [config.loadRelations, []], loadRelations: [config.loadRelations, []],
loadAttributes: [config.loadAttributes, []], loadAttributes: [config.loadAttributes, []],
loadCredentials: [config.loadCredentials, []],
removeOtherEntities: [config.removeOtherEntities, []], removeOtherEntities: [config.removeOtherEntities, []],
findExistingEntityByName: [config.findExistingEntityByName, []] findExistingEntityByName: [config.findExistingEntityByName, []]
}) })
@ -166,6 +169,7 @@ export class EntityTypesVersionLoadComponent extends PageComponent implements On
const config: EntityTypeVersionLoadConfig = { const config: EntityTypeVersionLoadConfig = {
loadAttributes: true, loadAttributes: true,
loadRelations: true, loadRelations: true,
loadCredentials: true,
removeOtherEntities: false, removeOtherEntities: false,
findExistingEntityByName: true findExistingEntityByName: true
}; };

5
ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.html

@ -38,6 +38,9 @@
{{ 'version-control.version-name-required' | translate }} {{ 'version-control.version-name-required' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<mat-checkbox *ngIf="entityId.entityType === entityTypes.DEVICE" formControlName="saveCredentials" style="margin-bottom: 16px;">
{{ 'version-control.export-credentials' | translate }}
</mat-checkbox>
<mat-checkbox formControlName="saveAttributes" style="margin-bottom: 16px;"> <mat-checkbox formControlName="saveAttributes" style="margin-bottom: 16px;">
{{ 'version-control.export-attributes' | translate }} {{ 'version-control.export-attributes' | translate }}
</mat-checkbox> </mat-checkbox>
@ -63,7 +66,7 @@
</div> </div>
</section> </section>
<section *ngIf="resultMessage"> <section *ngIf="resultMessage">
<div class="mat-title create-result-message">{{ resultMessage }}</div> <div class="mat-title vc-result-message">{{ resultMessage }}</div>
<div fxLayoutAlign="end center" fxLayoutGap="8px"> <div fxLayoutAlign="end center" fxLayoutGap="8px">
<button mat-button color="primary" <button mat-button color="primary"
type="button" type="button"

21
ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.scss

@ -1,21 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed 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.
*/
:host {
.create-result-message {
padding: 48px 8px 8px;
text-align: center;
}
}

22
ui-ngx/src/app/modules/home/components/vc/entity-version-create.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { import {
@ -28,11 +28,13 @@ import { EntitiesVersionControlService } from '@core/http/entities-version-contr
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { EntityType } from '@shared/models/entity-type.models';
import { TbPopoverComponent } from '@shared/components/popover.component';
@Component({ @Component({
selector: 'tb-entity-version-create', selector: 'tb-entity-version-create',
templateUrl: './entity-version-create.component.html', templateUrl: './entity-version-create.component.html',
styleUrls: ['./entity-version-create.component.scss'] styleUrls: ['./version-control.scss']
}) })
export class EntityVersionCreateComponent extends PageComponent implements OnInit { export class EntityVersionCreateComponent extends PageComponent implements OnInit {
@ -49,17 +51,20 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
onClose: (result: VersionCreationResult | null, branch: string | null) => void; onClose: (result: VersionCreationResult | null, branch: string | null) => void;
@Input() @Input()
onContentUpdated: () => void; onBeforeCreateVersion: () => Observable<any>;
@Input() @Input()
onBeforeCreateVersion: () => Observable<any>; popoverComponent: TbPopoverComponent;
createVersionFormGroup: FormGroup; createVersionFormGroup: FormGroup;
entityTypes = EntityType;
resultMessage: string; resultMessage: string;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private entitiesVersionControlService: EntitiesVersionControlService, private entitiesVersionControlService: EntitiesVersionControlService,
private cd: ChangeDetectorRef,
private translate: TranslateService, private translate: TranslateService,
private fb: FormBuilder) { private fb: FormBuilder) {
super(store); super(store);
@ -71,7 +76,8 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
versionName: [this.translate.instant('version-control.default-create-entity-version-name', versionName: [this.translate.instant('version-control.default-create-entity-version-name',
{entityName: this.entityName}), [Validators.required]], {entityName: this.entityName}), [Validators.required]],
saveRelations: [false, []], saveRelations: [false, []],
saveAttributes: [true, []] saveAttributes: [true, []],
saveCredentials: [true, []]
}); });
} }
@ -91,14 +97,16 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
config: { config: {
saveRelations: this.createVersionFormGroup.get('saveRelations').value, saveRelations: this.createVersionFormGroup.get('saveRelations').value,
saveAttributes: this.createVersionFormGroup.get('saveAttributes').value, saveAttributes: this.createVersionFormGroup.get('saveAttributes').value,
saveCredentials: this.entityId.entityType === EntityType.DEVICE ? this.createVersionFormGroup.get('saveCredentials').value : false
}, },
type: VersionCreateRequestType.SINGLE_ENTITY type: VersionCreateRequestType.SINGLE_ENTITY
}; };
this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => { this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => {
if (!result.added && !result.modified) { if (!result.added && !result.modified) {
this.resultMessage = this.translate.instant('version-control.nothing-to-commit'); this.resultMessage = this.translate.instant('version-control.nothing-to-commit');
if (this.onContentUpdated) { this.cd.detectChanges();
this.onContentUpdated(); if (this.popoverComponent) {
this.popoverComponent.updatePosition();
} }
} else if (this.onClose) { } else if (this.onClose) {
this.onClose(result, request.branch); this.onClose(result, request.branch);

4
ui-ngx/src/app/modules/home/components/vc/entity-version-diff.component.ts

@ -314,9 +314,9 @@ export class EntityVersionDiffComponent extends PageComponent implements OnInit,
versionName: this.versionName, versionName: this.versionName,
versionId: this.versionId, versionId: this.versionId,
externalEntityId: this.externalEntityId, externalEntityId: this.externalEntityId,
onClose: (result: Array<VersionLoadResult> | null) => { onClose: (result: VersionLoadResult | null) => {
restoreVersionPopover.hide(); restoreVersionPopover.hide();
if (result && result.length) { if (result && !result.error && result.result.length) {
this.close(); this.close();
this.versionRestored.emit(); this.versionRestored.emit();
} }

84
ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.html

@ -16,38 +16,54 @@
--> -->
<section [ngStyle]="entityDataInfo ? {minWidth: '400px'} : {}"> <section [ngStyle]="entityDataInfo ? {minWidth: '400px'} : {}">
<mat-toolbar *ngIf="entityDataInfo"> <section *ngIf="!errorMessage">
<h2>{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}</h2> <mat-toolbar *ngIf="entityDataInfo">
<span fxFlex></span> <h2>{{ 'version-control.restore-entity-from-version' | translate: {versionName} }}</h2>
</mat-toolbar> <span fxFlex></span>
<mat-progress-bar color="warn" style="z-index: 10; width: 100%; margin-bottom: -4px;" mode="indeterminate" </mat-toolbar>
*ngIf="entityDataInfo && (isLoading$ | async)"> <mat-progress-bar color="warn" style="z-index: 10; width: 100%; margin-bottom: -4px;" mode="indeterminate"
</mat-progress-bar> *ngIf="entityDataInfo && (isLoading$ | async)">
<mat-spinner *ngIf="!entityDataInfo" mode="indeterminate" diameter="80"></mat-spinner> </mat-progress-bar>
<form *ngIf="entityDataInfo" [formGroup]="restoreFormGroup" style="padding-top: 16px;"> <mat-spinner *ngIf="!entityDataInfo" mode="indeterminate" diameter="80"></mat-spinner>
<fieldset [disabled]="isLoading$ | async"> <form *ngIf="entityDataInfo" [formGroup]="restoreFormGroup" style="padding-top: 16px;">
<div fxFlex fxLayout="column"> <fieldset [disabled]="isLoading$ | async">
<mat-checkbox *ngIf="entityDataInfo.hasAttributes" formControlName="loadAttributes" style="margin-bottom: 16px;"> <div fxFlex fxLayout="column">
{{ 'version-control.load-attributes' | translate }} <mat-checkbox *ngIf="entityDataInfo.hasCredentials" formControlName="loadCredentials" style="margin-bottom: 16px;">
</mat-checkbox> {{ 'version-control.load-credentials' | translate }}
<mat-checkbox *ngIf="entityDataInfo.hasRelations" formControlName="loadRelations" style="margin-bottom: 16px;"> </mat-checkbox>
{{ 'version-control.load-relations' | translate }} <mat-checkbox *ngIf="entityDataInfo.hasAttributes" formControlName="loadAttributes" style="margin-bottom: 16px;">
</mat-checkbox> {{ 'version-control.load-attributes' | translate }}
</div> </mat-checkbox>
</fieldset> <mat-checkbox *ngIf="entityDataInfo.hasRelations" formControlName="loadRelations" style="margin-bottom: 16px;">
</form> {{ 'version-control.load-relations' | translate }}
<div *ngIf="entityDataInfo" fxLayoutAlign="end center" fxLayoutGap="8px"> </mat-checkbox>
<button mat-button color="primary" </div>
type="button" </fieldset>
[disabled]="(isLoading$ | async)" </form>
(click)="cancel()" cdkFocusInitial> <div *ngIf="entityDataInfo" fxLayoutAlign="end center" fxLayoutGap="8px">
{{ 'action.cancel' | translate }} <button mat-button color="primary"
</button> type="button"
<button mat-raised-button color="primary" [disabled]="(isLoading$ | async)"
type="button" (click)="cancel()" cdkFocusInitial>
(click)="restore()" {{ 'action.cancel' | translate }}
[disabled]="(isLoading$ | async) || restoreFormGroup.invalid"> </button>
{{ 'action.restore' | translate }} <button mat-raised-button color="primary"
</button> type="button"
</div> (click)="restore()"
[disabled]="(isLoading$ | async) || restoreFormGroup.invalid">
{{ 'action.restore' | translate }}
</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>
</section> </section>

25
ui-ngx/src/app/modules/home/components/vc/entity-version-restore.component.ts

@ -30,11 +30,12 @@ import { EntityId } from '@shared/models/id/entity-id';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { TbPopoverComponent } from '@shared/components/popover.component'; import { TbPopoverComponent } from '@shared/components/popover.component';
import { delay } from 'rxjs/operators'; import { delay } from 'rxjs/operators';
import { SafeHtml } from '@angular/platform-browser';
@Component({ @Component({
selector: 'tb-entity-version-restore', selector: 'tb-entity-version-restore',
templateUrl: './entity-version-restore.component.html', templateUrl: './entity-version-restore.component.html',
styleUrls: [] styleUrls: ['./version-control.scss']
}) })
export class EntityVersionRestoreComponent extends PageComponent implements OnInit { export class EntityVersionRestoreComponent extends PageComponent implements OnInit {
@ -51,7 +52,7 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
externalEntityId: EntityId; externalEntityId: EntityId;
@Input() @Input()
onClose: (result: Array<VersionLoadResult> | null) => void; onClose: (result: VersionLoadResult | null) => void;
@Input() @Input()
popoverComponent: TbPopoverComponent; popoverComponent: TbPopoverComponent;
@ -60,6 +61,8 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
restoreFormGroup: FormGroup; restoreFormGroup: FormGroup;
errorMessage: SafeHtml;
constructor(protected store: Store<AppState>, constructor(protected store: Store<AppState>,
private entitiesVersionControlService: EntitiesVersionControlService, private entitiesVersionControlService: EntitiesVersionControlService,
private cd: ChangeDetectorRef, private cd: ChangeDetectorRef,
@ -71,7 +74,8 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
ngOnInit(): void { ngOnInit(): void {
this.restoreFormGroup = this.fb.group({ this.restoreFormGroup = this.fb.group({
loadAttributes: [true, []], loadAttributes: [true, []],
loadRelations: [true, []] loadRelations: [true, []],
loadCredentials: [true, []]
}); });
this.entitiesVersionControlService.getEntityDataInfo(this.externalEntityId, this.versionId).subscribe((data) => { this.entitiesVersionControlService.getEntityDataInfo(this.externalEntityId, this.versionId).subscribe((data) => {
this.entityDataInfo = data; this.entityDataInfo = data;
@ -95,13 +99,22 @@ export class EntityVersionRestoreComponent extends PageComponent implements OnIn
externalEntityId: this.externalEntityId, externalEntityId: this.externalEntityId,
config: { config: {
loadRelations: this.entityDataInfo.hasRelations ? this.restoreFormGroup.get('loadRelations').value : false, loadRelations: this.entityDataInfo.hasRelations ? this.restoreFormGroup.get('loadRelations').value : false,
loadAttributes: this.entityDataInfo.hasAttributes ? this.restoreFormGroup.get('loadAttributes').value : false loadAttributes: this.entityDataInfo.hasAttributes ? this.restoreFormGroup.get('loadAttributes').value : false,
loadCredentials: this.entityDataInfo.hasCredentials ? this.restoreFormGroup.get('loadCredentials').value : false
}, },
type: VersionLoadRequestType.SINGLE_ENTITY type: VersionLoadRequestType.SINGLE_ENTITY
}; };
this.entitiesVersionControlService.loadEntitiesVersion(request).subscribe((result) => { this.entitiesVersionControlService.loadEntitiesVersion(request).subscribe((result) => {
if (this.onClose) { if (result.error) {
this.onClose(result); this.errorMessage = this.entitiesVersionControlService.entityLoadErrorToMessage(result.error);
this.cd.detectChanges();
if (this.popoverComponent) {
this.popoverComponent.updatePosition();
}
} else {
if (this.onClose) {
this.onClose(result);
}
} }
}); });
} }

4
ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.html

@ -15,8 +15,8 @@
limitations under the License. limitations under the License.
--> -->
<div class="mat-padding tb-entity-table tb-absolute-fill"> <div class="mat-padding tb-entity-table tb-absolute-fill" [ngClass]="{'tb-popover-mode': popoverComponent}">
<div fxFlex fxLayout="column" class="tb-entity-table-content" [ngClass]="{'mat-elevation-z1': !popoverMode}"> <div fxFlex fxLayout="column" class="tb-entity-table-content" [ngClass]="{'mat-elevation-z1': !popoverComponent}">
<mat-toolbar class="mat-table-toolbar" [fxShow]="!textSearchMode"> <mat-toolbar class="mat-table-toolbar" [fxShow]="!textSearchMode">
<div class="mat-toolbar-tools"> <div class="mat-toolbar-tools">
<div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container"> <div fxLayout="row" fxLayoutAlign="start center" fxLayout.xs="column" fxLayoutAlign.xs="center start" class="title-container">

7
ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.scss

@ -20,6 +20,13 @@
height: 100%; height: 100%;
display: block; display: block;
.tb-entity-table { .tb-entity-table {
&.tb-popover-mode {
position: relative;
width: 800px;
height: 600px;
}
.tb-entity-table-content { .tb-entity-table-content {
width: 100%; width: 100%;
height: 100%; height: 100%;

24
ui-ngx/src/app/modules/home/components/vc/entity-versions-table.component.ts

@ -49,6 +49,7 @@ import { EntityVersionRestoreComponent } from '@home/components/vc/entity-versio
import { EntityVersionDiffComponent } from '@home/components/vc/entity-version-diff.component'; import { EntityVersionDiffComponent } from '@home/components/vc/entity-version-diff.component';
import { ComplexVersionCreateComponent } from '@home/components/vc/complex-version-create.component'; import { ComplexVersionCreateComponent } from '@home/components/vc/complex-version-create.component';
import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version-load.component'; import { ComplexVersionLoadComponent } from '@home/components/vc/complex-version-load.component';
import { TbPopoverComponent } from '@shared/components/popover.component';
@Component({ @Component({
selector: 'tb-entity-versions-table', selector: 'tb-entity-versions-table',
@ -63,7 +64,7 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
singleEntityMode = false; singleEntityMode = false;
@Input() @Input()
popoverMode = false; popoverComponent: TbPopoverComponent;
@Input() @Input()
onBeforeCreateVersion: () => Observable<any>; onBeforeCreateVersion: () => Observable<any>;
@ -211,14 +212,9 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
this.updateData(); this.updateData();
} }
} }
},
onContentUpdated: () => {
createVersionPopover.updatePosition();
setTimeout(() => {
createVersionPopover.updatePosition();
});
} }
}, {}, {}, {}, false); }, {}, {}, {}, false);
createVersionPopover.tbComponentRef.instance.popoverComponent = createVersionPopover;
} }
} }
@ -243,14 +239,9 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
this.updateData(); this.updateData();
} }
} }
},
onContentUpdated: () => {
complexCreateVersionPopover.updatePosition();
setTimeout(() => {
complexCreateVersionPopover.updatePosition();
});
} }
}, {}, {}, {}, false); }, {}, {}, {}, false);
complexCreateVersionPopover.tbComponentRef.instance.popoverComponent = complexCreateVersionPopover;
} }
} }
@ -293,9 +284,9 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
versionName: entityVersion.name, versionName: entityVersion.name,
versionId: entityVersion.id, versionId: entityVersion.id,
externalEntityId: this.externalEntityIdValue, externalEntityId: this.externalEntityIdValue,
onClose: (result: Array<VersionLoadResult> | null) => { onClose: (result: VersionLoadResult | null) => {
restoreVersionPopover.hide(); restoreVersionPopover.hide();
if (result && result.length) { if (result && !result.error && result.result.length) {
this.versionRestored.emit(); this.versionRestored.emit();
} }
} }
@ -318,10 +309,11 @@ export class EntityVersionsTableComponent extends PageComponent implements OnIni
branch: this.branch, branch: this.branch,
versionName: entityVersion.name, versionName: entityVersion.name,
versionId: entityVersion.id, versionId: entityVersion.id,
onClose: (result: Array<VersionLoadResult> | null) => { onClose: (result: VersionLoadResult | null) => {
restoreEntitiesVersionPopover.hide(); restoreEntitiesVersionPopover.hide();
} }
}, {}, {}, {}, false); }, {}, {}, {}, false);
restoreEntitiesVersionPopover.tbComponentRef.instance.popoverComponent = restoreEntitiesVersionPopover;
} }
} }

2
ui-ngx/src/app/modules/home/components/vc/repository-settings.component.html

@ -16,7 +16,7 @@
--> -->
<div> <div>
<mat-card class="repository-settings" [ngClass]="{'settings-card': !detailsMode}"> <mat-card class="repository-settings" [ngClass]="{'settings-card': !detailsMode}" [ngStyle]="popoverComponent ? {boxShadow: 'none', width: '800px'} : {}">
<mat-card-title> <mat-card-title>
<div fxLayout="row"> <div fxLayout="row">
<span class="mat-headline" translate>admin.repository-settings</span> <span class="mat-headline" translate>admin.repository-settings</span>

7
ui-ngx/src/app/modules/home/components/vc/repository-settings.component.ts

@ -14,7 +14,7 @@
/// limitations under the License. /// limitations under the License.
/// ///
import { Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
import { PageComponent } from '@shared/components/page.component'; import { PageComponent } from '@shared/components/page.component';
import { FormBuilder, FormGroup, FormGroupDirective, Validators } from '@angular/forms'; import { FormBuilder, FormGroup, FormGroupDirective, Validators } from '@angular/forms';
import { select, Store } from '@ngrx/store'; import { select, Store } from '@ngrx/store';
@ -33,6 +33,7 @@ import { ActionAuthUpdateHasRepository } from '@core/auth/auth.actions';
import { selectHasRepository } from '@core/auth/auth.selectors'; import { selectHasRepository } from '@core/auth/auth.selectors';
import { catchError, mergeMap, take } from 'rxjs/operators'; import { catchError, mergeMap, take } from 'rxjs/operators';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { TbPopoverComponent } from '@shared/components/popover.component';
@Component({ @Component({
selector: 'tb-repository-settings', selector: 'tb-repository-settings',
@ -44,6 +45,9 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit
@Input() @Input()
detailsMode = false; detailsMode = false;
@Input()
popoverComponent: TbPopoverComponent;
repositorySettingsForm: FormGroup; repositorySettingsForm: FormGroup;
settings: RepositorySettings = null; settings: RepositorySettings = null;
@ -61,6 +65,7 @@ export class RepositorySettingsComponent extends PageComponent implements OnInit
private adminService: AdminService, private adminService: AdminService,
private dialogService: DialogService, private dialogService: DialogService,
private translate: TranslateService, private translate: TranslateService,
private cd: ChangeDetectorRef,
public fb: FormBuilder) { public fb: FormBuilder) {
super(store); super(store);
} }

3
ui-ngx/src/app/modules/home/components/vc/version-control.component.html

@ -16,11 +16,12 @@
--> -->
<tb-repository-settings #repositorySettingsComponent [detailsMode]="detailsMode" <tb-repository-settings #repositorySettingsComponent [detailsMode]="detailsMode"
[popoverComponent]="popoverComponent"
*ngIf="!(hasRepository$ | async); else versionsTable"> *ngIf="!(hasRepository$ | async); else versionsTable">
</tb-repository-settings> </tb-repository-settings>
<ng-template #versionsTable> <ng-template #versionsTable>
<tb-entity-versions-table [singleEntityMode]="singleEntityMode" <tb-entity-versions-table [singleEntityMode]="singleEntityMode"
[popoverMode]="popoverMode" [popoverComponent]="popoverComponent"
[onBeforeCreateVersion]="onBeforeCreateVersion" [onBeforeCreateVersion]="onBeforeCreateVersion"
[active]="active" [active]="active"
[entityId]="entityId" [entityId]="entityId"

3
ui-ngx/src/app/modules/home/components/vc/version-control.component.ts

@ -23,6 +23,7 @@ import { RepositorySettingsComponent } from '@home/components/vc/repository-sett
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { EntityId } from '@shared/models/id/entity-id'; import { EntityId } from '@shared/models/id/entity-id';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { TbPopoverComponent } from '@shared/components/popover.component';
@Component({ @Component({
selector: 'tb-version-control', selector: 'tb-version-control',
@ -37,7 +38,7 @@ export class VersionControlComponent implements OnInit, HasConfirmForm {
detailsMode = false; detailsMode = false;
@Input() @Input()
popoverMode = false; popoverComponent: TbPopoverComponent;
@Input() @Input()
active = true; active = true;

6
ui-ngx/src/app/modules/home/components/vc/complex-version-load.component.scss → ui-ngx/src/app/modules/home/components/vc/version-control.scss

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
:host { :host {
.load-result-message { .vc-result-message {
padding: 0 8px; padding: 0 8px;
text-align: center; text-align: center;
&:first-child { &:first-child {
@ -23,5 +23,9 @@
&:nth-last-child(2) { &:nth-last-child(2) {
padding-bottom: 8px; padding-bottom: 8px;
} }
&.error {
text-align: start;
font-weight: 400;
}
} }
} }

4
ui-ngx/src/app/modules/home/pages/rulechain/rulechain-page.component.ts

@ -1545,7 +1545,6 @@ export class RuleChainPageComponent extends PageComponent
this.viewContainerRef, VersionControlComponent, 'leftTop', true, null, this.viewContainerRef, VersionControlComponent, 'leftTop', true, null,
{ {
detailsMode: true, detailsMode: true,
popoverMode: true,
active: true, active: true,
singleEntityMode: true, singleEntityMode: true,
externalEntityId: this.ruleChain.externalId || this.ruleChain.id, externalEntityId: this.ruleChain.externalId || this.ruleChain.id,
@ -1558,7 +1557,8 @@ export class RuleChainPageComponent extends PageComponent
return of(null); return of(null);
} }
} }
}, {width: '800px', height: '600px'}, {}, {}, true); }, {}, {}, {}, true);
versionControlPopover.tbComponentRef.instance.popoverComponent = versionControlPopover;
versionControlPopover.tbComponentRef.instance.versionRestored.subscribe(() => { versionControlPopover.tbComponentRef.instance.versionRestored.subscribe(() => {
this.reloadRuleChain(); this.reloadRuleChain();
}); });

30
ui-ngx/src/app/shared/models/vc.models.ts

@ -33,6 +33,7 @@ export const exportableEntityTypes: Array<EntityType> = [
export interface VersionCreateConfig { export interface VersionCreateConfig {
saveRelations: boolean; saveRelations: boolean;
saveAttributes: boolean; saveAttributes: boolean;
saveCredentials: boolean;
} }
export enum VersionCreateRequestType { export enum VersionCreateRequestType {
@ -90,6 +91,7 @@ export function createDefaultEntityTypesVersionCreate(): {[entityType: string]:
syncStrategy: null, syncStrategy: null,
saveAttributes: true, saveAttributes: true,
saveRelations: true, saveRelations: true,
saveCredentials: true,
allEntities: true, allEntities: true,
entityIds: [] entityIds: []
}; };
@ -100,6 +102,7 @@ export function createDefaultEntityTypesVersionCreate(): {[entityType: string]:
export interface VersionLoadConfig { export interface VersionLoadConfig {
loadRelations: boolean; loadRelations: boolean;
loadAttributes: boolean; loadAttributes: boolean;
loadCredentials: boolean;
} }
export enum VersionLoadRequestType { export enum VersionLoadRequestType {
@ -135,6 +138,7 @@ export function createDefaultEntityTypesVersionLoad(): {[entityType: string]: En
res[entityType] = { res[entityType] = {
loadAttributes: true, loadAttributes: true,
loadRelations: true, loadRelations: true,
loadCredentials: true,
removeOtherEntities: false, removeOtherEntities: false,
findExistingEntityByName: true findExistingEntityByName: true
}; };
@ -161,13 +165,36 @@ export interface VersionCreationResult {
removed: number; removed: number;
} }
export interface VersionLoadResult { export interface EntityTypeLoadResult {
entityType: EntityType; entityType: EntityType;
created: number; created: number;
updated: number; updated: number;
deleted: number; deleted: number;
} }
export enum EntityLoadErrorType {
DEVICE_CREDENTIALS_CONFLICT = 'DEVICE_CREDENTIALS_CONFLICT',
MISSING_REFERENCED_ENTITY = 'MISSING_REFERENCED_ENTITY'
}
export const entityLoadErrorTranslationMap = new Map<EntityLoadErrorType, string>(
[
[EntityLoadErrorType.DEVICE_CREDENTIALS_CONFLICT, 'version-control.device-credentials-conflict'],
[EntityLoadErrorType.MISSING_REFERENCED_ENTITY, 'version-control.missing-referenced-entity']
]
);
export interface EntityLoadError {
type: EntityLoadErrorType;
source: EntityId;
target: EntityId;
}
export interface VersionLoadResult {
result: Array<EntityTypeLoadResult>;
error: EntityLoadError;
}
export interface EntityExportData<E extends ExportableEntity<EntityId>> { export interface EntityExportData<E extends ExportableEntity<EntityId>> {
entity: E; entity: E;
entityType: EntityType; entityType: EntityType;
@ -195,4 +222,5 @@ export function entityExportDataToJsonString(data: EntityExportData<any>): strin
export interface EntityDataInfo { export interface EntityDataInfo {
hasRelations: boolean; hasRelations: boolean;
hasAttributes: boolean; hasAttributes: boolean;
hasCredentials: boolean;
} }

8
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -3127,6 +3127,7 @@
"author": "Author", "author": "Author",
"export-relations": "Export relations", "export-relations": "Export relations",
"export-attributes": "Export attributes", "export-attributes": "Export attributes",
"export-credentials": "Export credentials",
"entity-versions": "Entity versions", "entity-versions": "Entity versions",
"versions": "Versions", "versions": "Versions",
"created-time": "Created time", "created-time": "Created time",
@ -3140,6 +3141,7 @@
"restore-entity-from-version": "Restore entity from version '{{versionName}}'", "restore-entity-from-version": "Restore entity from version '{{versionName}}'",
"load-relations": "Load relations", "load-relations": "Load relations",
"load-attributes": "Load attributes", "load-attributes": "Load attributes",
"load-credentials": "Load credentials",
"show-version-diff": "Show version diff", "show-version-diff": "Show version diff",
"diff-entity-with-version": "Diff with entity version '{{versionName}}'", "diff-entity-with-version": "Diff with entity version '{{versionName}}'",
"previous-difference": "Previous Difference", "previous-difference": "Previous Difference",
@ -3158,7 +3160,7 @@
"no-entities-to-restore-prompt": "Please specify entities to restore", "no-entities-to-restore-prompt": "Please specify entities to restore",
"add-entity-type": "Add entity type", "add-entity-type": "Add entity type",
"remove-all": "Remove all", "remove-all": "Remove all",
"version-create-result": "{ added, plural, 0 {No entities} 1 {1 entity} other {# entities} } added.\n{ modified, plural, 0 {No entities} 1 {1 entity} other {# entities} } modified.\n{ removed, plural, 0 {No entities} 1 {1 entity} other {# entities} } removed.", "version-create-result": "{ added, plural, 0 {No entities} 1 {1 entity} other {# entities} } added.<br/>{ modified, plural, 0 {No entities} 1 {1 entity} other {# entities} } modified.<br/>{ removed, plural, 0 {No entities} 1 {1 entity} other {# entities} } removed.",
"remove-other-entities": "Remove other entities", "remove-other-entities": "Remove other entities",
"find-existing-entity-by-name": "Find existing entity by name", "find-existing-entity-by-name": "Find existing entity by name",
"restore-entities-from-version": "Restore entities from version '{{versionName}}'", "restore-entities-from-version": "Restore entities from version '{{versionName}}'",
@ -3170,7 +3172,9 @@
"auto-commit-to-branch": "auto-commit to <b>{{ branch }}</b> branch", "auto-commit-to-branch": "auto-commit to <b>{{ branch }}</b> branch",
"default-create-entity-version-name": "{{entityName}} update", "default-create-entity-version-name": "{{entityName}} update",
"sync-strategy-merge-hint": "Sync strategy <b>merge</b> hint", "sync-strategy-merge-hint": "Sync strategy <b>merge</b> hint",
"sync-strategy-overwrite-hint": "Sync strategy <b>overwrite</b> hint" "sync-strategy-overwrite-hint": "Sync strategy <b>overwrite</b> hint",
"device-credentials-conflict": "Failed to load the device with external id <b>{{entityId}}</b><br/>due to the same credentials are already present in the database for another device.<br/>Please consider disabling the <b>load credentials</b> setting in the restore form.",
"missing-referenced-entity": "Failed to load the <b>{{sourceEntityTypeName}}</b> with external id <b>{{sourceEntityId}}</b><br/>because it references missing <b>{{targetEntityTypeName}}</b> with id <b>{{targetEntityId}}</b>."
}, },
"widget": { "widget": {
"widget-library": "Widgets Library", "widget-library": "Widgets Library",

Loading…
Cancel
Save