From b29d706a51a2f0db0d8ff1d8f7d7802489de8307 Mon Sep 17 00:00:00 2001 From: Denys Butenko Date: Thu, 5 Dec 2024 22:02:23 +0700 Subject: [PATCH] NAS-132767: Fix payload for cloud sync task --- .../cloudsync-what-and-when.component.spec.ts | 26 +++++++++++ .../cloudsync-what-and-when.component.ts | 20 +++++++-- src/assets/i18n/pl.json | 44 +++++++++---------- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-wizard/steps/cloudsync-what-and-when/cloudsync-what-and-when.component.spec.ts b/src/app/pages/data-protection/cloudsync/cloudsync-wizard/steps/cloudsync-what-and-when/cloudsync-what-and-when.component.spec.ts index c8a8b002787..a06bccc9cb8 100644 --- a/src/app/pages/data-protection/cloudsync/cloudsync-wizard/steps/cloudsync-what-and-when/cloudsync-what-and-when.component.spec.ts +++ b/src/app/pages/data-protection/cloudsync/cloudsync-wizard/steps/cloudsync-what-and-when/cloudsync-what-and-when.component.spec.ts @@ -8,6 +8,8 @@ import { of } from 'rxjs'; import { mockAuth } from 'app/core/testing/utils/mock-auth.utils'; import { mockCall, mockWebSocket } from 'app/core/testing/utils/mock-websocket.utils'; import { DialogService } from 'app/modules/dialog/dialog.service'; +import { IxInputHarness } from 'app/modules/forms/ix-forms/components/ix-input/ix-input.harness'; +import { IxSelectHarness } from 'app/modules/forms/ix-forms/components/ix-select/ix-select.harness'; import { ChainedRef } from 'app/modules/forms/ix-forms/components/ix-slide-in/chained-component-ref'; import { IxSlideInRef } from 'app/modules/forms/ix-forms/components/ix-slide-in/ix-slide-in-ref'; import { IxFormsModule } from 'app/modules/forms/ix-forms/ix-forms.module'; @@ -135,4 +137,28 @@ describe('CloudSyncWhatAndWhenComponent', () => { }); expect(chainedRef.swap).toHaveBeenCalledWith(CloudSyncFormComponent, true); }); + + it('checks payload when use invalid s3 credentials', async () => { + const bucketSelect = await loader.getHarness(IxSelectHarness.with({ label: 'Bucket' })); + expect(await bucketSelect.getValue()).toBe(''); + + spectator.component.isCredentialInvalid$.next(true); + spectator.detectChanges(); + + const bucketInput = await loader.getHarness(IxInputHarness.with({ label: 'Bucket' })); + await bucketInput.setValue('selected'); + + expect(spectator.component.getPayload()).toEqual(expect.objectContaining({ + attributes: expect.objectContaining({ + bucket: 'selected', + }), + })); + + await bucketInput.setValue('test-bucket'); + expect(spectator.component.getPayload()).toEqual(expect.objectContaining({ + attributes: expect.objectContaining({ + bucket: 'test-bucket', + }), + })); + }); }); diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-wizard/steps/cloudsync-what-and-when/cloudsync-what-and-when.component.ts b/src/app/pages/data-protection/cloudsync/cloudsync-wizard/steps/cloudsync-what-and-when/cloudsync-what-and-when.component.ts index d6857c5cdfe..053c532d305 100644 --- a/src/app/pages/data-protection/cloudsync/cloudsync-wizard/steps/cloudsync-what-and-when/cloudsync-what-and-when.component.ts +++ b/src/app/pages/data-protection/cloudsync/cloudsync-wizard/steps/cloudsync-what-and-when/cloudsync-what-and-when.component.ts @@ -8,6 +8,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService } from '@ngx-translate/core'; import _ from 'lodash'; import { + BehaviorSubject, EMPTY, Observable, catchError, combineLatest, filter, map, merge, of, tap, } from 'rxjs'; @@ -88,6 +89,7 @@ export class CloudSyncWhatAndWhenComponent implements OnInit, OnChanges { bwlimit: [[] as string[]], }); + isCredentialInvalid$ = new BehaviorSubject(false); credentials: CloudSyncCredential[] = []; providers: CloudSyncProvider[] = []; bucketPlaceholder: string = helptextCloudSync.bucket_placeholder; @@ -242,6 +244,8 @@ export class CloudSyncWhatAndWhenComponent implements OnInit, OnChanges { if (formValue[name] !== undefined && formValue[name] !== null && formValue[name] !== '') { if (name === 'task_encryption') { attributes[name] = formValue[name] === '' ? null : formValue[name]; + } else if (name === 'bucket_input') { + attributes['bucket'] = formValue[name]; } else { attributes[name] = formValue[name]; } @@ -407,6 +411,16 @@ export class CloudSyncWhatAndWhenComponent implements OnInit, OnChanges { } }); + this.isCredentialInvalid$.pipe(untilDestroyed(this)).subscribe((value) => { + if (value) { + this.form.controls.bucket_input.enable(); + this.form.controls.bucket.disable(); + } else { + this.form.controls.bucket_input.disable(); + this.form.controls.bucket.enable(); + } + }); + this.form.controls.bucket.valueChanges.pipe( filter((selectedOption) => selectedOption === newOption), untilDestroyed(this), @@ -448,13 +462,11 @@ export class CloudSyncWhatAndWhenComponent implements OnInit, OnChanges { }); } this.bucketOptions$ = of(bucketOptions); - this.form.controls.bucket.enable(); - this.form.controls.bucket_input.disable(); + this.isCredentialInvalid$.next(false); this.cdr.markForCheck(); }, error: (error: WebSocketError) => { - this.form.controls.bucket.disable(); - this.form.controls.bucket_input.enable(); + this.isCredentialInvalid$.next(true); this.dialog.closeAllDialogs(); this.dialog.confirm({ title: error.extra ? (error.extra as { excerpt: string }).excerpt : `${this.translate.instant('Error: ')}${error.error}`, diff --git a/src/assets/i18n/pl.json b/src/assets/i18n/pl.json index 9b407057678..cf5f08dfa50 100644 --- a/src/assets/i18n/pl.json +++ b/src/assets/i18n/pl.json @@ -4880,7 +4880,6 @@ "dRAID1": "", "dRAID2": "", "dRAID3": "", - "details": "", "disk stats": "", "disk writes": "", "everyone@": "", @@ -4905,21 +4904,14 @@ "lzjb (legacy, not recommended)": "", "mountd(8) bind port": "", "never ran": "", - "of": "", "on this enclosure.": "", - "or": "", - "owner@": "", "pCloud": "", "pbkdf2iters": "", "pigz (all rounder)": "", "plzip (best compression)": "", - "readonly": "", "rpc.lockd(8) bind port": "", "rpc.statd(8) bind port": "", - "standby": "", - "to another TrueNAS": "", - "to cloud": "", - "total available": "", + "was successfully attached.": "", "zle (runs of zeros)": "", "zstd (default level, 3)": "", "zstd-5 (slow)": "", @@ -4970,8 +4962,6 @@ "{n, plural, one {Failed Disk} other {Failed Disks}}": "", "{n, plural, one {Pool in Enclosure} other {Pools in Enclosure}}": "", "{n, plural, one {SAS Expander} other {SAS Expanders}}": "", - "{name} Devices": "", - "{name} Sessions": "", "{name} and {n, plural, one {# other pool} other {# other pools}} are not healthy.": "", "{nic} Address": "", "{n} (applies to descendants)": "", @@ -4993,17 +4983,7 @@ "{temp}°C (Core #{core})": "", "{temp}°C ({coreCount} cores at {temp}°C)": "", "{threadCount, plural, one {# thread} other {# threads} }": "", - "{type} VDEVs": "", - "{type} at {location}": "", - "{type} widget does not support {size} size.": "", - "{type} widget is not supported.": "", "{type} | {vdevWidth} wide | ": "", - "{usage}% (All Threads)": "", - "{usage}% (Thread #{thread})": "", - "{usage}% ({threadCount} threads at {usage}%)": "", - "{used} of {total} ({used_pct})": "", - "{version} is available!": "", - "{view} on {enclosure}": "", " seconds.": " sekund", "% of all cores": "% użycia wszytskich rdzeni", "(24 Hours)": "(24 Godziny)", @@ -5118,5 +5098,25 @@ "Wipe": "Wyczyść", "Wipe this disk?": "Czy wyczyścić ten dysk?", "[Use fewer transactions in exchange for more RAM.](https://rclone.org/docs/#fast-list) This can also speed up or slow down the transfer.": "[Use fewer transactions in exchange for more RAM.](https://rclone.org/docs/\\#fast-list) This can also speed up or slow down the transfer.", - "was successfully attached.": "został pomyślanie powiązany." + "details": "szczegóły", + "of": "z", + "or": "lub", + "owner@": "właściciel@", + "readonly": "tylko do odczytu", + "standby": "czuwanie", + "to another TrueNAS": "do innego TrueNAS", + "to cloud": "do chmury", + "total available": "łącznie dostępne", + "{name} Devices": "Urządzenia", + "{name} Sessions": "Sesji", + "{type} VDEVs": "{type} VDEVs", + "{type} at {location}": "{type} w {location}", + "{type} widget does not support {size} size.": "Widżet {type} nie obsługuje rozmiaru {size}", + "{type} widget is not supported.": "Widżet {type} nie jest obsługiwany.", + "{usage}% (All Threads)": "{usage}% Wszystkich wątków)", + "{usage}% (Thread #{thread})": "{usage}% (Wątek #{thread})", + "{usage}% ({threadCount} threads at {usage}%)": "{usage}% ({threadCount} wątków przy {usage}%)", + "{used} of {total} ({used_pct})": "{used} z {total} ({used_pct})", + "{version} is available!": "{version} jest dostępna!", + "{view} on {enclosure}": "{view} na {enclosure}" } \ No newline at end of file