Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAS-133394 / 25.04 / More strict null fixes #11283

Merged
merged 2 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/app/interfaces/enclosure.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,17 @@ export interface DashboardEnclosureSlot {
descriptor: string;
status: EnclosureStatus;
dev: string | null;
supports_identify_light?: boolean;
supports_identify_light: boolean;
drive_bay_light_status: DriveBayLightStatus | null;
size?: number | null;
model?: string | null;
size: number | null;
model: string | null;
is_top: boolean;
is_front: boolean;
is_rear: boolean;
is_internal: boolean;
serial?: string | null;
type?: DiskType | null;
rotationrate?: number | null;
serial: string | null;
type: DiskType | null;
rotationrate: number | null;
pool_info: EnclosureSlotPoolInfo | null;
}

Expand Down
4 changes: 2 additions & 2 deletions src/app/modules/auth/two-factor-guard.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ describe('TwoFactorGuardService', () => {
let spectator: SpectatorService<TwoFactorGuardService>;

const isAuthenticated$ = new BehaviorSubject(false);
const userTwoFactorConfig$ = new BehaviorSubject(null as UserTwoFactorConfig);
const getGlobalTwoFactorConfig = jest.fn(() => of(null as GlobalTwoFactorConfig));
const userTwoFactorConfig$ = new BehaviorSubject<UserTwoFactorConfig | null>(null);
const getGlobalTwoFactorConfig = jest.fn(() => of(null as GlobalTwoFactorConfig | null));
const hasRole$ = new BehaviorSubject(false);

const createService = createServiceFactory({
Expand Down
2 changes: 1 addition & 1 deletion src/app/modules/empty/empty.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { EmptyConfig } from 'app/interfaces/empty-config.interface';
export class EmptyService {
constructor(private translate: TranslateService) { }

defaultEmptyConfig(type: EmptyType): EmptyConfig {
defaultEmptyConfig(type?: EmptyType | null): EmptyConfig {
switch (type) {
case EmptyType.Loading:
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export class IxComboboxComponent implements ControlValueAccessor, OnInit {
});

private readonly inputElementRef: Signal<ElementRef<HTMLInputElement>> = viewChild.required('ixInput', { read: ElementRef });
private readonly autoCompleteRef = viewChild('auto', { read: MatAutocomplete });
private readonly autoCompleteRef = viewChild.required('auto', { read: MatAutocomplete });
private readonly autocompleteTrigger = viewChild(MatAutocompleteTrigger);

options: Option[] = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class IxInputComponent implements ControlValueAccessor, OnInit, OnChanges
/** If formatted value returned by parseAndFormatInput has non-numeric letters
* and input 'type' is a number, the input will stay empty on the form */
readonly format = input<(value: string | number) => string>();
readonly parse = input<(value: string | number) => string | number>();
readonly parse = input<(value: string | number) => string | number | null>();

readonly inputElementRef: Signal<ElementRef<HTMLInputElement>> = viewChild.required('ixInput', { read: ElementRef });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ export class FormErrorHandlerService {
const extra = (error as ApiError).extra as string[][];
for (const extraItem of extra) {
const field = extraItem[0].split('.').pop();
if (!field) {
return;
}

const errorMessage = extraItem[1];

const control = this.getFormField(formGroup, field, fieldsMap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@ export function matchOthersFgValidator(
}
}
if (errFields.length) {
fg.get(controlName).setErrors({
subjectControl.setErrors({
matchOther: errMsg ? { message: errMsg } : true,
});
return {
[controlName]: { matchOther: errMsg ? { message: errMsg } : true },
};
}
let prevErrors = { ...fg.get(controlName).errors };
let prevErrors = { ...subjectControl.errors };
delete prevErrors.matchOther;
if (isEmpty(prevErrors)) {
prevErrors = null;
}
fg.get(controlName).setErrors(prevErrors);
subjectControl.setErrors(prevErrors);
return null;
};
}
Expand Down Expand Up @@ -66,19 +66,19 @@ export function doesNotEqualFgValidator(
}
}
if (errFields.length) {
fg.get(controlName).setErrors({
subjectControl.setErrors({
matchesOther: errMsg ? { message: errMsg } : true,
});
return {
[controlName]: { matchesOther: errMsg ? { message: errMsg } : true },
};
}
let prevErrors = { ...fg.get(controlName).errors };
let prevErrors = { ...subjectControl.errors };
delete prevErrors.matchesOther;
if (isEmpty(prevErrors)) {
prevErrors = null;
}
fg.get(controlName).setErrors(prevErrors);
subjectControl.setErrors(prevErrors);
return null;
};
}
4 changes: 2 additions & 2 deletions src/app/modules/jobs/store/job.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import { jobIndicatorPressed } from 'app/store/topbar/topbar.actions';
export interface JobsState extends EntityState<Job> {
isLoading: boolean;
isPanelOpen: boolean;
error: string;
error: string | null;
}

export const adapter = createEntityAdapter<Job>({
selectId: (job) => job.id,
sortComparer: (a, b) => b.time_started.$date - a.time_started.$date,
});

export const jobsInitialState = adapter.getInitialState({
export const jobsInitialState: JobsState = adapter.getInitialState({
isLoading: false,
isPanelOpen: false,
error: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
}
<h3 class="ix-form-title">
{{ title() | translate }}
@if (requiredRoles()?.length && !(hasRequiredRoles | async)) {
@if (requiredRoles().length && !(hasRequiredRoles | async)) {
<ix-readonly-badge></ix-readonly-badge>
}
</h3>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="ix-slidein-title-bar">
<h3 class="ix-formtitle">
{{ title() | translate }}
@if (requiredRoles()?.length && !(hasRequiredRoles() | async)) {
@if (requiredRoles().length && !(hasRequiredRoles() | async)) {
<ix-readonly-badge></ix-readonly-badge>
}
</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,10 @@ export class CustomAppFormComponent implements OnInit {
).subscribe({
next: () => {
this.slideInRef.close({ response: true, error: null });
if (this.isNew()) {
this.router.navigate(['/apps', 'installed']);
} else {
if (this.existingApp) {
this.router.navigate(['/apps', 'installed', this.existingApp.metadata.train, this.existingApp.name]);
} else {
this.router.navigate(['/apps', 'installed']);
}
},
error: (error: unknown) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<a
class="installed-badge"
[matTooltip]="'An instance of this app already installed. Click the badge to see installed apps.' | translate"
[ixTest]="[app()?.name, 'installed-badge']"
[ixTest]="[app().name, 'installed-badge']"
(click)="navigateToAllInstalledPage($event)"
>
{{ 'Installed' | translate }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
],
})
export class GoogleDriveProviderFormComponent extends BaseProviderFormComponent implements AfterViewInit {
private readonly oauthComponent = viewChild(OauthProviderComponent);
private readonly oauthComponent = viewChild.required(OauthProviderComponent);

form = this.formBuilder.group({
token: ['', Validators.required],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
],
})
export class PcloudProviderFormComponent extends BaseProviderFormComponent implements AfterViewInit {
private readonly oauthComponent = viewChild(OauthProviderComponent);
private readonly oauthComponent = viewChild.required(OauthProviderComponent);

form = this.formBuilder.nonNullable.group({
token: ['', Validators.required],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,16 @@ export class SshKeypairFormComponent implements OnInit {

this.isFormLoading = true;
let request$: Observable<unknown>;
if (this.isNew) {
request$ = this.api.call('keychaincredential.create', [{
...commonBody,
type: KeychainCredentialType.SshKeyPair,
}]);
} else {
if (this.editingKeypair) {
request$ = this.api.call('keychaincredential.update', [
this.editingKeypair.id,
commonBody,
]);
} else {
request$ = this.api.call('keychaincredential.create', [{
...commonBody,
type: KeychainCredentialType.SshKeyPair,
}]);
}

request$.pipe(untilDestroyed(this)).subscribe({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,13 @@ export class AcmednsFormComponent implements OnInit {
this.isLoading = true;
let request$: Observable<unknown>;

if (this.isNew) {
request$ = this.api.call('acme.dns.authenticator.create', [values]);
} else {
if (this.editingAcmedns) {
request$ = this.api.call('acme.dns.authenticator.update', [
this.editingAcmedns.id,
values,
]);
} else {
request$ = this.api.call('acme.dns.authenticator.create', [values]);
}

request$.pipe(untilDestroyed(this)).subscribe({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export class CertificateOptionsComponent implements OnInit, OnChanges, SummaryPr
hasSignedBy = input(false);
hasLifetime = input(false);

form = this.formBuilder.group({
signedby: [null as number],
form = this.formBuilder.nonNullable.group({
signedby: [null as number | null],
key_type: [CertificateKeyType.Rsa],
key_length: [2048],
ec_curve: ['BrainpoolP384R1'],
Expand Down Expand Up @@ -90,7 +90,7 @@ export class CertificateOptionsComponent implements OnInit, OnChanges, SummaryPr
}

getSummary(): SummarySection {
const values = this.form.value;
const values = this.form.getRawValue();
const signingAuthority = this.signingAuthorities.find((option) => option.value === values.signedby);

const summary: SummarySection = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,28 +131,28 @@ export class GroupFormComponent implements OnInit {
setupForm(): void {
this.setFormRelations();

if (this.isNew) {
this.api.call('group.get_next_gid').pipe(untilDestroyed(this)).subscribe((nextId) => {
this.form.patchValue({
gid: nextId,
});
this.cdr.markForCheck();
});
this.setNamesInUseValidator();
} else {
if (this.editingGroup) {
this.form.controls.gid.disable();
this.form.patchValue({
gid: this.editingGroup.gid,
name: this.editingGroup.group,
sudo_commands: this.editingGroup.sudo_commands.includes(allCommands) ? [] : this.editingGroup.sudo_commands,
sudo_commands_all: this.editingGroup.sudo_commands.includes(allCommands),
sudo_commands: this.editingGroup.sudo_commands?.includes(allCommands) ? [] : this.editingGroup.sudo_commands,
sudo_commands_all: !!this.editingGroup.sudo_commands?.includes(allCommands),
sudo_commands_nopasswd: this.editingGroup.sudo_commands_nopasswd?.includes(allCommands)
? []
: this.editingGroup.sudo_commands_nopasswd,
sudo_commands_nopasswd_all: this.editingGroup.sudo_commands_nopasswd?.includes(allCommands),
smb: this.editingGroup.smb,
});
this.setNamesInUseValidator(this.editingGroup.group);
} else {
this.api.call('group.get_next_gid').pipe(untilDestroyed(this)).subscribe((nextId) => {
this.form.patchValue({
gid: nextId,
});
this.cdr.markForCheck();
});
this.setNamesInUseValidator();
}
}

Expand All @@ -167,16 +167,16 @@ export class GroupFormComponent implements OnInit {

this.isFormLoading = true;
let request$: Observable<unknown>;
if (this.isNew) {
request$ = this.api.call('group.create', [{
...commonBody,
gid: values.gid,
}]);
} else {
if (this.editingGroup) {
request$ = this.api.call('group.update', [
this.editingGroup.id,
commonBody,
]);
} else {
request$ = this.api.call('group.create', [{
...commonBody,
gid: values.gid,
}]);
}

request$.pipe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class PrivilegeFormComponent implements OnInit {

protected readonly helptext = helptextPrivilege;
protected readonly isEnterprise = toSignal(this.store$.select(selectIsEnterprise));
protected existingPrivilege: Privilege;
protected existingPrivilege: Privilege | undefined;

get isNew(): boolean {
return !this.existingPrivilege;
Expand Down Expand Up @@ -143,21 +143,21 @@ export class PrivilegeFormComponent implements OnInit {

ngOnInit(): void {
if (this.existingPrivilege) {
this.setPrivilegeForEdit();
this.setPrivilegeForEdit(this.existingPrivilege);
if (this.existingPrivilege.builtin_name) {
this.form.controls.name.disable();
this.form.controls.roles.disable();
}
}
}

setPrivilegeForEdit(): void {
setPrivilegeForEdit(existingPrivilege: Privilege): void {
this.form.patchValue({
...this.existingPrivilege,
local_groups: this.existingPrivilege.local_groups.map(
...existingPrivilege,
local_groups: existingPrivilege.local_groups.map(
(group) => group.group || this.translate.instant('Missing group - {gid}', { gid: group.gid }),
),
ds_groups: this.existingPrivilege.ds_groups.map((group) => group.group),
ds_groups: existingPrivilege.ds_groups.map((group) => group.group),
});
this.cdr.markForCheck();
}
Expand All @@ -171,10 +171,10 @@ export class PrivilegeFormComponent implements OnInit {

this.isLoading = true;
let request$: Observable<Privilege>;
if (this.isNew) {
request$ = this.api.call('privilege.create', [values]);
} else {
if (this.existingPrivilege) {
request$ = this.api.call('privilege.update', [this.existingPrivilege.id, values]);
} else {
request$ = this.api.call('privilege.create', [values]);
}

request$.pipe(
Expand Down
Loading
Loading