Skip to content

Commit

Permalink
NAS-128947 / 24.10 / Replacing more usages of EntityJobComponent (#10050
Browse files Browse the repository at this point in the history
)
  • Loading branch information
undsoft committed May 13, 2024
1 parent fea96e0 commit 8cacafa
Show file tree
Hide file tree
Showing 98 changed files with 308 additions and 224 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ When it is set, system will rename the existing \
directory/file path where the dataset should be mounted resulting in successful unlock of the dataset.'),
dataset_passphrase_validation: [Validators.minLength(8)],
fetching_encryption_summary_title: T('Fetching Encryption Summary'),
fetching_encryption_summary_message: T('Fetching Encryption Summary for '),
fetching_encryption_summary_message: T('Fetching Encryption Summary for {dataset}'),
unlocking_datasets_title: T('Unlocking Datasets'),
unlock_dataset_dialog: {
title: T('Unlock Datasets'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,17 @@ describe('DatasetUnlockComponent', () => {
let spectator: Spectator<DatasetUnlockComponent>;
let loader: HarnessLoader;

const encryptionSummary = fakeSuccessfulJob([
{ name: 'pool_name_1', key_format: DatasetEncryptionType.Default },
{ name: 'pool_name_2', key_format: DatasetEncryptionType.Passphrase },
] as DatasetEncryptionSummary[]);

const mockDialogRef = {
componentInstance: {
setDescription: jest.fn(),
setCall: jest.fn(),
submit: jest.fn(),
success: of(fakeSuccessfulJob([
{ name: 'pool_name_1', key_format: DatasetEncryptionType.Default },
{ name: 'pool_name_2', key_format: DatasetEncryptionType.Passphrase },
] as DatasetEncryptionSummary[])),
success: of(encryptionSummary),
failure: of(),
wspost: jest.fn(),
},
Expand All @@ -47,15 +49,18 @@ describe('DatasetUnlockComponent', () => {
],
providers: [
mockAuth(),
mockProvider(DialogService),
mockProvider(Router),
mockProvider(ActivatedRoute, {
snapshot: { params: { datasetId: 'pool_name_1' } },
}),
mockWebSocket([
mockJob('pool.dataset.encryption_summary'),
]),
mockProvider(DialogService),
mockProvider(DialogService, {
jobDialog: () => ({
afterClosed: () => of(encryptionSummary),
}),
}),
mockProvider(MatDialog, {
open: jest.fn(() => mockDialogRef),
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { EntityJobComponent } from 'app/modules/entity/entity-job/entity-job.com
import { UnlockSummaryDialogComponent } from 'app/pages/datasets/modules/encryption/components/unlock-summary-dialog/unlock-summary-dialog.component';
import { AuthService } from 'app/services/auth/auth.service';
import { ErrorHandlerService } from 'app/services/error-handler.service';
import { WebSocketService } from 'app/services/ws.service';

interface DatasetFormGroup {
key?: FormControl<string>;
Expand Down Expand Up @@ -73,6 +74,7 @@ export class DatasetUnlockComponent implements OnInit {
private apiEndPoint: string;

constructor(
private ws: WebSocketService,
private formBuilder: FormBuilder,
protected aroute: ActivatedRoute,
private authService: AuthService,
Expand Down Expand Up @@ -113,70 +115,61 @@ export class DatasetUnlockComponent implements OnInit {
}

getEncryptionSummary(): void {
const dialogRef = this.matDialog.open(EntityJobComponent, {
data: { title: helptextUnlock.fetching_encryption_summary_title },
disableClose: true,
});
dialogRef.componentInstance.setDescription(
this.translate.instant(helptextUnlock.fetching_encryption_summary_message) + this.pk,
);
dialogRef.componentInstance.setCall('pool.dataset.encryption_summary', [this.pk]);
dialogRef.componentInstance.submit();
dialogRef.componentInstance.success.pipe(untilDestroyed(this)).subscribe({
next: (job: Job<DatasetEncryptionSummary[]>) => {
if (!job) {
return;
}
this.dialogService.jobDialog(
this.ws.job('pool.dataset.encryption_summary', [this.pk]),
{
title: this.translate.instant(helptextUnlock.fetching_encryption_summary_title),
description: this.translate.instant(helptextUnlock.fetching_encryption_summary_message, { dataset: this.pk }),
},
)
.afterClosed()
.pipe(
this.errorHandler.catchError(),
untilDestroyed(this),
)
.subscribe((job) => {
this.processSummary(job.result);
});
}

dialogRef.close();
if (job.result && job.result.length > 0) {
for (let i = 0; i < job.result.length; i++) {
const result = job.result[i];
const isPassphrase = result.key_format === DatasetEncryptionType.Passphrase;
if (this.form.controls.datasets.controls[i] === undefined) {
if (isPassphrase) {
this.form.controls.datasets.push(this.formBuilder.group({
name: [''],
passphrase: ['', [Validators.minLength(8)]],
is_passphrase: [true],
}) as FormGroup<DatasetFormGroup>);
} else {
this.form.controls.datasets.push(this.formBuilder.group({
name: [''],
key: ['', [Validators.minLength(64), Validators.maxLength(64)]],
file: [null as File[]],
is_passphrase: [false],
}) as FormGroup<DatasetFormGroup>);
}
private processSummary(summary: DatasetEncryptionSummary[]): void {
if (!summary?.length) {
return;
}

(this.form.controls.datasets.controls[i].controls.file as FormControl)?.valueChanges.pipe(
switchMap((files: File[]) => (!files?.length ? of('') : from(files[0].text()))),
untilDestroyed(this),
).subscribe((key) => {
(this.form.controls.datasets.controls[i].controls.key as FormControl).setValue(key);
});
}
this.form.controls.datasets.disable();
(this.form.controls.datasets.controls[i].controls.name as FormControl).setValue(result.name);
(this.form.controls.datasets.controls[i].controls.is_passphrase as FormControl).setValue(isPassphrase);
}
this.hideFileInput = this.form.controls.datasets.value.every(
(dataset) => dataset.is_passphrase,
);
this.form.controls.use_file.setValue(!this.hideFileInput);
}
},
error: this.handleError,
});
dialogRef.componentInstance.failure.pipe(untilDestroyed(this)).subscribe({
next: (error) => {
if (error) {
dialogRef.close();
this.handleError(error);
summary.forEach((result, i) => {
const isPassphrase = result.key_format === DatasetEncryptionType.Passphrase;
if (this.form.controls.datasets.controls[i] === undefined) {
if (isPassphrase) {
this.form.controls.datasets.push(this.formBuilder.group({
name: [''],
passphrase: ['', [Validators.minLength(8)]],
is_passphrase: [true],
}) as FormGroup<DatasetFormGroup>);
} else {
this.form.controls.datasets.push(this.formBuilder.group({
name: [''],
key: ['', [Validators.minLength(64), Validators.maxLength(64)]],
file: [null as File[]],
is_passphrase: [false],
}) as FormGroup<DatasetFormGroup>);
}
},
error: this.handleError,

(this.form.controls.datasets.controls[i].controls.file as FormControl)?.valueChanges.pipe(
switchMap((files: File[]) => (!files?.length ? of('') : from(files[0].text()))),
untilDestroyed(this),
).subscribe((key) => {
(this.form.controls.datasets.controls[i].controls.key as FormControl).setValue(key);
});
}
this.form.controls.datasets.disable();
(this.form.controls.datasets.controls[i].controls.name as FormControl).setValue(result.name);
(this.form.controls.datasets.controls[i].controls.is_passphrase as FormControl).setValue(isPassphrase);
});
this.hideFileInput = this.form.controls.datasets.value.every(
(dataset) => dataset.is_passphrase,
);
this.form.controls.use_file.setValue(!this.hideFileInput);
}

handleError = (error: WebSocketError | Job): void => {
Expand Down Expand Up @@ -248,7 +241,7 @@ export class DatasetUnlockComponent implements OnInit {
disableClose: true,
});
dialogRef.componentInstance.setDescription(
this.translate.instant(helptextUnlock.fetching_encryption_summary_message) + this.pk,
this.translate.instant(helptextUnlock.fetching_encryption_summary_message, { dataset: this.pk }),
);

if (values.use_file) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {
ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit,
ChangeDetectionStrategy, Component, OnInit,
} from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { Store } from '@ngrx/store';
Expand Down Expand Up @@ -77,7 +77,6 @@ export class IscsiCardComponent implements OnInit {
private ws: WebSocketService,
private dialogService: DialogService,
protected emptyService: EmptyService,
private cdr: ChangeDetectorRef,
private store$: Store<ServicesState>,
) {}

Expand Down
18 changes: 11 additions & 7 deletions src/app/services/fips.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import {
createServiceFactory,
mockProvider,
SpectatorService,
} from '@ngneat/spectator/jest';
import { of } from 'rxjs';
import { mockEntityJobComponentRef } from 'app/core/testing/utils/mock-entity-job-component-ref.utils';
import { fakeSuccessfulJob } from 'app/core/testing/utils/fake-job.utils';
import { mockJob, mockWebSocket } from 'app/core/testing/utils/mock-websocket.utils';
import { DialogService } from 'app/modules/dialog/dialog.service';
import { FipsService } from 'app/services/fips.service';
import { WebSocketService } from 'app/services/ws.service';

describe('FipsService', () => {
let spectator: SpectatorService<FipsService>;
const createService = createServiceFactory({
service: FipsService,
providers: [
mockProvider(MatDialog, {
open: jest.fn(() => mockEntityJobComponentRef),
}),
mockProvider(DialogService, {
confirm: jest.fn(() => of(true)),
jobDialog: jest.fn(() => ({
afterClosed: () => of({}),
})),
}),
mockProvider(Router),
mockWebSocket([
mockJob('failover.reboot.other_node', fakeSuccessfulJob()),
]),
],
});

Expand Down Expand Up @@ -64,8 +68,8 @@ describe('FipsService', () => {
buttonText: 'Restart Standby',
}),
);
expect(mockEntityJobComponentRef.componentInstance.setCall).toHaveBeenCalledWith('failover.reboot.other_node');
expect(mockEntityJobComponentRef.componentInstance.submit).toHaveBeenCalled();
expect(spectator.inject(WebSocketService).job).toHaveBeenCalledWith('failover.reboot.other_node');
expect(spectator.inject(DialogService).jobDialog).toHaveBeenCalled();
});
});
});
41 changes: 21 additions & 20 deletions src/app/services/fips.service.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Router } from '@angular/router';
import { untilDestroyed } from '@ngneat/until-destroy';
import { TranslateService } from '@ngx-translate/core';
import { Observable, of, switchMap } from 'rxjs';
import {
filter, tap,
} from 'rxjs/operators';
import { DialogService } from 'app/modules/dialog/dialog.service';
import { EntityJobComponent } from 'app/modules/entity/entity-job/entity-job.component';
import { SnackbarService } from 'app/modules/snackbar/services/snackbar.service';
import { ErrorHandlerService } from 'app/services/error-handler.service';
import { WebSocketService } from 'app/services/ws.service';

@Injectable({
providedIn: 'root',
Expand All @@ -25,8 +24,9 @@ export class FipsService {
private dialog: DialogService,
private translate: TranslateService,
private router: Router,
private matDialog: MatDialog,
private snackbar: SnackbarService,
private ws: WebSocketService,
private errorHandler: ErrorHandlerService,
) {}

promptForRestart(): Observable<unknown> {
Expand Down Expand Up @@ -64,7 +64,7 @@ export class FipsService {
);
}

promptForRemoteRestart(): Observable<boolean> {
promptForRemoteRestart(): Observable<unknown> {
return of(this.isRemotePromptOpen).pipe(
filter((isOpen) => !isOpen),
switchMap(() => {
Expand All @@ -75,26 +75,27 @@ export class FipsService {
buttonText: this.translate.instant('Restart Standby'),
});
}),
tap((approved) => {
switchMap((approved) => {
this.isRemotePromptOpen = false;
if (approved) {
this.restartRemote();
if (!approved) {
return of({});
}
return this.restartRemote();
}),
);
}

// TODO: Change to return Observable.
private restartRemote(): void {
const dialogRef = this.matDialog.open(EntityJobComponent, {
data: { title: this.translate.instant('Restarting Standby') },
disableClose: true,
});
dialogRef.componentInstance.setCall('failover.reboot.other_node');
dialogRef.componentInstance.submit();
dialogRef.componentInstance.success.pipe(untilDestroyed(this)).subscribe(() => {
this.snackbar.success(this.translate.instant('System Security Settings Updated.'));
dialogRef.close();
});
private restartRemote(): Observable<unknown> {
return this.dialog.jobDialog(
this.ws.job('failover.reboot.other_node'),
{ title: this.translate.instant('Restarting Standby') },
)
.afterClosed()
.pipe(
this.errorHandler.catchError(),
tap(() => {
this.snackbar.success(this.translate.instant('System Security Settings Updated.'));
}),
);
}
}
11 changes: 5 additions & 6 deletions src/app/store/ha-upgrade/ha-upgrade.effects.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { MatDialog } from '@angular/material/dialog';
import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator/jest';
import { provideMockActions } from '@ngrx/effects/testing';
import { provideMockStore } from '@ngrx/store/testing';
import { firstValueFrom, of, ReplaySubject } from 'rxjs';
import { fakeSuccessfulJob } from 'app/core/testing/utils/fake-job.utils';
import { mockAuth } from 'app/core/testing/utils/mock-auth.utils';
import { mockEntityJobComponentRef } from 'app/core/testing/utils/mock-entity-job-component-ref.utils';
import { mockCall, mockJob, mockWebSocket } from 'app/core/testing/utils/mock-websocket.utils';
import { FailoverDisabledReason } from 'app/enums/failover-disabled-reason.enum';
import { DialogService } from 'app/modules/dialog/dialog.service';
Expand All @@ -27,6 +25,9 @@ describe('HaUpgradeEffects', () => {
providers: [
mockProvider(DialogService, {
confirm: jest.fn(() => of(true)),
jobDialog: jest.fn(() => ({
afterClosed: () => of({}),
})),
}),
mockWebSocket([
mockCall('failover.upgrade_pending', true),
Expand All @@ -41,9 +42,6 @@ describe('HaUpgradeEffects', () => {
],
}),
provideMockActions(() => actions$),
mockProvider(MatDialog, {
open: jest.fn(() => mockEntityJobComponentRef),
}),
mockAuth(),
],
});
Expand Down Expand Up @@ -120,7 +118,8 @@ describe('HaUpgradeEffects', () => {
actions$.next(updatePendingIndicatorPressed());
await firstValueFrom(spectator.service.showUpgradePendingDialog$);

expect(mockEntityJobComponentRef.componentInstance.setCall).toHaveBeenCalledWith('failover.upgrade_finish');
expect(spectator.inject(DialogService).jobDialog).toHaveBeenCalled();
expect(spectator.inject(WebSocketService).job).toHaveBeenCalledWith('failover.upgrade_finish');

expect(await firstValueFrom(spectator.service.showUpgradePendingDialog$))
.toEqual(failoverUpgradeFinished());
Expand Down

0 comments on commit 8cacafa

Please sign in to comment.