UI: Implement progress on single entity version create request.
This commit is contained in:
		
							parent
							
								
									5c240469b2
								
							
						
					
					
						commit
						5e9899daad
					
				@ -82,6 +82,7 @@ import org.thingsboard.server.service.sync.vc.data.SimpleEntitiesExportCtx;
 | 
			
		||||
import java.nio.ByteBuffer;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
 | 
			
		||||
@ -433,7 +434,7 @@ public abstract class BaseExportImportServiceTest extends AbstractControllerTest
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected <E extends ExportableEntity<I>, I extends EntityId> EntityImportResult<E> importEntity(User user, EntityExportData<E> exportData, EntityImportSettings importSettings) throws Exception {
 | 
			
		||||
        EntitiesImportCtx ctx = new EntitiesImportCtx(getSecurityUser(user), null, importSettings);
 | 
			
		||||
        EntitiesImportCtx ctx = new EntitiesImportCtx(UUID.randomUUID(), getSecurityUser(user), null, importSettings);
 | 
			
		||||
        ctx.setFinalImportAttempt(true);
 | 
			
		||||
        exportData = JacksonUtil.treeToValue(JacksonUtil.valueToTree(exportData), EntityExportData.class);
 | 
			
		||||
        EntityImportResult<E> importResult = exportImportService.importEntity(ctx, exportData);
 | 
			
		||||
 | 
			
		||||
@ -17,13 +17,19 @@
 | 
			
		||||
import { Injectable } from '@angular/core';
 | 
			
		||||
import { HttpClient } from '@angular/common/http';
 | 
			
		||||
import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
 | 
			
		||||
import { Observable, of } from 'rxjs';
 | 
			
		||||
import { Observable, of, timer } from 'rxjs';
 | 
			
		||||
import {
 | 
			
		||||
  BranchInfo, EntityDataDiff, EntityDataInfo, EntityLoadError, entityLoadErrorTranslationMap, EntityLoadErrorType,
 | 
			
		||||
  BranchInfo,
 | 
			
		||||
  EntityDataDiff,
 | 
			
		||||
  EntityDataInfo,
 | 
			
		||||
  EntityLoadError,
 | 
			
		||||
  entityLoadErrorTranslationMap,
 | 
			
		||||
  EntityLoadErrorType,
 | 
			
		||||
  EntityVersion,
 | 
			
		||||
  VersionCreateRequest,
 | 
			
		||||
  VersionCreationResult,
 | 
			
		||||
  VersionLoadRequest, VersionLoadResult
 | 
			
		||||
  VersionLoadRequest,
 | 
			
		||||
  VersionLoadResult
 | 
			
		||||
} from '@shared/models/vc.models';
 | 
			
		||||
import { PageLink } from '@shared/models/page/page-link';
 | 
			
		||||
import { PageData } from '@shared/models/page/page-data';
 | 
			
		||||
@ -32,9 +38,10 @@ 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, tap } from 'rxjs/operators';
 | 
			
		||||
import { catchError, finalize, 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';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -83,16 +90,30 @@ export class EntitiesVersionControlService {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public saveEntitiesVersion(request: VersionCreateRequest, config?: RequestConfig): Observable<VersionCreationResult> {
 | 
			
		||||
    return this.http.post<VersionCreationResult>('/api/entities/vc/version', request, defaultHttpOptionsFromConfig(config)).pipe(
 | 
			
		||||
      tap(() => {
 | 
			
		||||
    this.store.dispatch(new ActionLoadStart());
 | 
			
		||||
    return this.http.post<string>('/api/entities/vc/version', request,
 | 
			
		||||
      defaultHttpOptionsFromConfig({...config, ...{ignoreLoading: true}})).pipe(
 | 
			
		||||
      switchMap((requestId) => {
 | 
			
		||||
        return timer(0, 2000).pipe(
 | 
			
		||||
          switchMap(() => this.getVersionCreateRequestStatus(requestId, config)),
 | 
			
		||||
          takeWhile((res) => !res.done, true)
 | 
			
		||||
        );
 | 
			
		||||
      }),
 | 
			
		||||
      finalize(() => {
 | 
			
		||||
        const branch = request.branch;
 | 
			
		||||
        if (this.branchList && !this.branchList.find(b => b.name === branch)) {
 | 
			
		||||
          this.branchList = null;
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
        this.store.dispatch(new ActionLoadFinish());
 | 
			
		||||
      }),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private getVersionCreateRequestStatus(requestId: string, config?: RequestConfig): Observable<VersionCreationResult> {
 | 
			
		||||
    return this.http.get<VersionCreationResult>(`/api/entities/vc/version/${requestId}/status`,
 | 
			
		||||
      defaultHttpOptionsFromConfig({...config, ...{ignoreLoading: true}}));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public listEntityVersions(pageLink: PageLink, branch: string,
 | 
			
		||||
                            externalEntityId: EntityId,
 | 
			
		||||
                            config?: RequestConfig): Observable<PageData<EntityVersion>> {
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<section style="min-width: 400px;">
 | 
			
		||||
  <section *ngIf="!resultMessage">
 | 
			
		||||
  <section *ngIf="!versionCreateResult$">
 | 
			
		||||
    <mat-toolbar>
 | 
			
		||||
      <h2>{{ 'version-control.create-entity-version' | translate }}</h2>
 | 
			
		||||
      <span fxFlex></span>
 | 
			
		||||
@ -65,15 +65,27 @@
 | 
			
		||||
      </button>
 | 
			
		||||
    </div>
 | 
			
		||||
  </section>
 | 
			
		||||
  <section *ngIf="resultMessage">
 | 
			
		||||
    <div class="mat-title vc-result-message">{{ resultMessage }}</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="versionCreateResult$">
 | 
			
		||||
    <section *ngIf="(versionCreateResult$ | async)?.done; else progress">
 | 
			
		||||
      <section *ngIf="resultMessage">
 | 
			
		||||
        <div class="mat-title vc-result-message">{{ resultMessage }}</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.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 {
 | 
			
		||||
@ -27,16 +27,17 @@ import { AppState } from '@core/core.state';
 | 
			
		||||
import { EntitiesVersionControlService } from '@core/http/entities-version-control.service';
 | 
			
		||||
import { EntityId } from '@shared/models/id/entity-id';
 | 
			
		||||
import { TranslateService } from '@ngx-translate/core';
 | 
			
		||||
import { Observable, of } from 'rxjs';
 | 
			
		||||
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';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-entity-version-create',
 | 
			
		||||
  templateUrl: './entity-version-create.component.html',
 | 
			
		||||
  styleUrls: ['./version-control.scss']
 | 
			
		||||
})
 | 
			
		||||
export class EntityVersionCreateComponent extends PageComponent implements OnInit {
 | 
			
		||||
export class EntityVersionCreateComponent extends PageComponent implements OnInit, OnDestroy {
 | 
			
		||||
 | 
			
		||||
  @Input()
 | 
			
		||||
  branch: string;
 | 
			
		||||
@ -62,6 +63,10 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
 | 
			
		||||
 | 
			
		||||
  resultMessage: string;
 | 
			
		||||
 | 
			
		||||
  versionCreateResult$: Observable<VersionCreationResult>;
 | 
			
		||||
 | 
			
		||||
  private versionCreateResultSubscription: Subscription;
 | 
			
		||||
 | 
			
		||||
  constructor(protected store: Store<AppState>,
 | 
			
		||||
              private entitiesVersionControlService: EntitiesVersionControlService,
 | 
			
		||||
              private cd: ChangeDetectorRef,
 | 
			
		||||
@ -81,6 +86,13 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy() {
 | 
			
		||||
    super.ngOnDestroy();
 | 
			
		||||
    if (this.versionCreateResultSubscription) {
 | 
			
		||||
      this.versionCreateResultSubscription.unsubscribe();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  cancel(): void {
 | 
			
		||||
    if (this.onClose) {
 | 
			
		||||
      this.onClose(null, null);
 | 
			
		||||
@ -101,18 +113,27 @@ export class EntityVersionCreateComponent extends PageComponent implements OnIni
 | 
			
		||||
        },
 | 
			
		||||
        type: VersionCreateRequestType.SINGLE_ENTITY
 | 
			
		||||
      };
 | 
			
		||||
      this.entitiesVersionControlService.saveEntitiesVersion(request).subscribe((result) => {
 | 
			
		||||
        if (!result.added && !result.modified) {
 | 
			
		||||
          this.resultMessage = this.translate.instant('version-control.nothing-to-commit');
 | 
			
		||||
          this.cd.detectChanges();
 | 
			
		||||
          if (this.popoverComponent) {
 | 
			
		||||
            this.popoverComponent.updatePosition();
 | 
			
		||||
      this.versionCreateResult$ = this.entitiesVersionControlService.saveEntitiesVersion(request).pipe(
 | 
			
		||||
        share()
 | 
			
		||||
      );
 | 
			
		||||
      this.cd.detectChanges();
 | 
			
		||||
      if (this.popoverComponent) {
 | 
			
		||||
        this.popoverComponent.updatePosition();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      this.versionCreateResultSubscription = this.versionCreateResult$.subscribe((result) => {
 | 
			
		||||
        if (result.done) {
 | 
			
		||||
          if (!result.added && !result.modified || result.error) {
 | 
			
		||||
            this.resultMessage = result.error ? result.error : this.translate.instant('version-control.nothing-to-commit');
 | 
			
		||||
            this.cd.detectChanges();
 | 
			
		||||
            if (this.popoverComponent) {
 | 
			
		||||
              this.popoverComponent.updatePosition();
 | 
			
		||||
            }
 | 
			
		||||
          } else if (this.onClose) {
 | 
			
		||||
            this.onClose(result, request.branch);
 | 
			
		||||
          }
 | 
			
		||||
        } else if (this.onClose) {
 | 
			
		||||
          this.onClose(result, request.branch);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -27,5 +27,8 @@
 | 
			
		||||
      text-align: start;
 | 
			
		||||
      font-weight: 400;
 | 
			
		||||
    }
 | 
			
		||||
    &.progress {
 | 
			
		||||
      padding-bottom: 32px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -165,6 +165,8 @@ export interface VersionCreationResult {
 | 
			
		||||
  added: number;
 | 
			
		||||
  modified: number;
 | 
			
		||||
  removed: number;
 | 
			
		||||
  error: string;
 | 
			
		||||
  done: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface EntityTypeLoadResult {
 | 
			
		||||
 | 
			
		||||
@ -3306,6 +3306,7 @@
 | 
			
		||||
        "no-versions-text": "No versions found",
 | 
			
		||||
        "copy-full-version-id": "Copy full version id",
 | 
			
		||||
        "create-version": "Create version",
 | 
			
		||||
        "creating-version": "Creating version... Please wait",
 | 
			
		||||
        "nothing-to-commit": "No changes to commit",
 | 
			
		||||
        "restore-version": "Restore version",
 | 
			
		||||
        "restore-entity-from-version": "Restore entity from version '{{versionName}}'",
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user