Skip to content

Commit

Permalink
Add db version check
Browse files Browse the repository at this point in the history
  • Loading branch information
richard-cox committed Apr 16, 2020
1 parent c218907 commit ca7929b
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ import {
BaseEndpointConfig,
} from './backup-restore-endpoints.service';

interface BackupEndpointConfigRequest extends BaseEndpointConfig {
// connectedUser: string;
}

interface BackupEndpointRequestData {
state: BackupEndpointsConfig<BackupEndpointConfigRequest>;

interface BackupRequest {
state: BackupEndpointsConfig<BaseEndpointConfig>;
userId: string;
password: string;
}
Expand Down Expand Up @@ -144,10 +142,10 @@ export class BackupEndpointsService extends BackupRestoreEndpointService {
);
}

private createBodyToSend(sd: SessionData): BackupEndpointRequestData {
const state: BackupEndpointsConfig<BackupEndpointConfigRequest> = Object.entries(this.state).reduce((res, [endpointId, endpoint]) => {
private createBodyToSend(sd: SessionData): BackupRequest {
const state: BackupEndpointsConfig<BaseEndpointConfig> = Object.entries(this.state).reduce((res, [endpointId, endpoint]) => {
const { entity, ...rest } = endpoint;
const requestConfig: BackupEndpointConfigRequest = {
const requestConfig: BaseEndpointConfig = {
...rest,
};
res[endpointId] = requestConfig;
Expand All @@ -156,7 +154,7 @@ export class BackupEndpointsService extends BackupRestoreEndpointService {
return {
state,
userId: this.getUserIdFromSessionData(sd),
password: this.password
password: this.password,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,79 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { BehaviorSubject, Observable } from 'rxjs';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

import { GeneralEntityAppState } from '../../../../../store/src/app-state';
import { selectSessionData } from '../../../../../store/src/reducers/auth.reducer';
import { SessionData } from '../../../../../store/src/types/auth.types';
import { LoggerService } from '../../../core/logger.service';
import { BrowserStandardEncoder } from '../../../helper';
import { BackupRestoreEndpointService } from './backup-restore-endpoints.service';

interface BackupContent {
payload: string;
dbVersion: number;
}

interface RestoreEndpointsData {
data: string;
password: string;
ignoreDbVersion: boolean;
}

@Injectable()
export class RestoreEndpointsService extends BackupRestoreEndpointService {

validFile = new BehaviorSubject(false);
validFile$ = this.validFile.asObservable();
// Step 1
validFileContent = new BehaviorSubject(false);
validFileContent$: Observable<boolean> = this.validFileContent.asObservable();

file = new BehaviorSubject<{
name: string,
content: BackupContent
}>(null);
file$ = this.file.asObservable();

validDb = new BehaviorSubject(false);
validDb$: Observable<boolean>;
currentDbVersion$: Observable<number>;
ignoreDbVersion = new BehaviorSubject(false);
ignoreDbVersion$ = this.ignoreDbVersion.asObservable();

// Step 2
password: string; // TODO: RC use set password in both services
fileName: string;
private fileContent: string;

constructor(
private store: Store<GeneralEntityAppState>,
private http: HttpClient
private http: HttpClient,
private logger: LoggerService
) {
super();
this.setupStep1();
}

private setupStep1() {
this.currentDbVersion$ = this.store.select(selectSessionData()).pipe(
map((sd: SessionData) => sd.version.database_version)
);

this.validDb$ = combineLatest([
this.file$,
this.currentDbVersion$
]).pipe(
map(([file, currentDbVersion]) => {
return file && file.content.dbVersion === currentDbVersion;
})
);

this.validFileContent$ = combineLatest([
this.file$,
this.validDb$,
this.ignoreDbVersion$
]).pipe(
map(([file, validDb, ignoreDb]) => !!file && (ignoreDb || validDb))
);
}

setFile(file): Promise<string> {
Expand All @@ -43,17 +91,28 @@ export class RestoreEndpointsService extends BackupRestoreEndpointService {
}

private setFileResult(content: string, fileName: string) {
if (!!content) {
this.validFile.next(true);
this.fileName = fileName;
this.fileContent = content;
let parsedContent: BackupContent;
try {
parsedContent = JSON.parse(content);
} catch (e) {
this.logger.warn('Failed to parse file contents: ', e);
}

if (!!parsedContent) {
this.file.next({
name: fileName,
content: parsedContent
});
} else {
this.validFile.next(false);
this.fileName = '';
this.fileContent = '';
this.file.next(null);
}

// this.updateFileContentValidation();
}

setIgnoreDbVersion(ignore: boolean) {
this.ignoreDbVersion.next(ignore);
}

restoreBackup(): Observable<any> {
const url = '/pp/v1/endpoints/restore';
Expand All @@ -62,15 +121,19 @@ export class RestoreEndpointsService extends BackupRestoreEndpointService {
fromObject,
encoder: new BrowserStandardEncoder()
});
return this.http.post(url, this.createBodyToSend(), {
params
});
}

createBodyToSend(): RestoreEndpointsData {
return {
data: this.fileContent,
password: this.password
};
return combineLatest([
this.file$,
this.ignoreDbVersion$
]).pipe(
switchMap(([file, ignoreDb]) => {
const body: RestoreEndpointsData = {
data: JSON.stringify(file.content),
password: this.password,
ignoreDbVersion: ignoreDb
};
return this.http.post(url, body, { params });
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,27 @@ <h1>Restore Endpoints</h1>
</app-page-header>

<app-steppers cancel="/endpoints" [basePreviousRedirect]="{ path: '/endpoints/backup-restore' }">
<app-step title="File" [valid]="fileValid$ | async">
<app-step title="File" [valid]="service.validFileContent$ | async">
<div class="file-step">
<p>Provide the backup file to restore from.</p>
<div class="file-step__input">
<input #localPathSelectFile fileread="localPathFile" type="file" class="file-step__input--input"
name="localPathSelectFile" id="localPathSelectFile" (change)="onFileChange($event)" />Choose
<button (click)="localPathSelectFile.click()" color="primary" mat-button mat-raised-button>File</button>
{{service.fileName}}
<div class="file-step__chunk">
<p>Provide the backup file to restore from.</p>
<div class="file-step__input">
<input #localPathSelectFile fileread="localPathFile" type="file" class="file-step__input--input"
name="localPathSelectFile" id="localPathSelectFile" (change)="onFileChange($event)" />Choose
<button (click)="localPathSelectFile.click()" color="primary" mat-button mat-raised-button>File</button>
<ng-container *ngIf="service.file$ | async as file">{{file.name}}</ng-container>
</div>
</div>
<ng-container *ngIf="service.file$ | async as file">
<div *ngIf="!(service.validDb$ | async)" class="file-step__chunk">
<p>The database version of Stratos (<code>{{service.currentDbVersion$ | async}}</code>) and the backup
(<code>{{file.content.dbVersion}}</code>) are different. Restoring this file may have adverse affects.
</p>
<mat-checkbox (change)="onIgnoreDbChange($event)">
Ignore different database versions
</mat-checkbox>
</div>
</ng-container>
</div>
</app-step>
<app-step title="Password" finishButtonText="Restore" [onNext]="restore" [valid]="passwordValid$ | async">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
flex: 1;
}
.file-step {
display: flex;
flex-direction: column;

&,
&__chunk {
display: flex;
flex-direction: column;
}

&__input {
&--input {
height: 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Component, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { Store } from '@ngrx/store';
import { MatCheckboxChange } from '@angular/material';
import { Observable, of, Subject } from 'rxjs';
import { first, map, tap } from 'rxjs/operators';

import { AppState } from '../../../../../../store/src/app-state';
import { PaginationMonitorFactory } from '../../../../../../store/src/monitors/pagination-monitor.factory';
import { getEventFiles } from '../../../../core/browser-helper';
import { ConfirmationDialogConfig } from '../../../../shared/components/confirmation-dialog.config';
import { ConfirmationDialogService } from '../../../../shared/components/confirmation-dialog.service';
Expand All @@ -21,41 +19,17 @@ import { RestoreEndpointsService } from '../restore-endpoints.service';
RestoreEndpointsService
]
})
export class RestoreEndpointsComponent implements OnInit {

// Step 1
fileValid$: Observable<boolean>;
fileName: string;
export class RestoreEndpointsComponent {

// Step 2
passwordValid$: Observable<boolean>;
passwordForm: FormGroup;

constructor(
public service: RestoreEndpointsService,
store: Store<AppState>,
paginationMonitorFactory: PaginationMonitorFactory,
private confirmDialog: ConfirmationDialogService,
) {

// const endpoints$ = of([]);
// this.endpointDataSource = {
// isTableLoading$: of(false),
// connect: () => endpoints$.pipe(
// map(endpoints => endpoints.sort((a, b) => a.name.localeCompare(b.name)))
// ),
// disconnect: () => { },
// trackBy: (index, row) => row.guid
// };

this.setupFileStep();

this.setupPasswordStep();

}

setupFileStep() {
this.fileValid$ = this.service.validFile$;
}

setupPasswordStep() {
Expand All @@ -70,33 +44,17 @@ export class RestoreEndpointsComponent implements OnInit {
);
}

ngOnInit() {
}

onFileChange(event) {
const files = getEventFiles(event);
if (!files.length) {
return;
}
const file = files[0];
console.log(event, file);
this.service.setFile(file);
}

// console.log(files);
// TODO: RC file load - get content of file
// TODO: RC file load - validate correct file
// TODO: RC file load - parse file
// TODO: RC file load - enable next step


// const utils = new DeployApplicationFsUtils();
// utils.handleFileInputSelection(files).pipe(
// filter(res => !!res),
// first()
// ).subscribe((res) => {
// this.propagateChange(res);
// this.sourceData$.next(res);
// });
onIgnoreDbChange(event: MatCheckboxChange) {
this.service.setIgnoreDbVersion(event.checked);
}

restore: StepOnNextFunction = () => {
Expand Down
2 changes: 1 addition & 1 deletion src/frontend/packages/store/src/reducers/auth.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,5 @@ export function authReducer(state: AuthState = defaultState, action): AuthState
}

export function selectSessionData() {
return (state: AuthOnlyAppState) => state.auth.sessionData;
return (state: AuthOnlyAppState): SessionData => state.auth.sessionData;
}
Loading

0 comments on commit ca7929b

Please sign in to comment.