mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 23:03:32 +00:00
Vault Strict Typing cleanup (#12512)
* remove strict types from `NewDeviceVerificationNotice` - Add null default value for class properties - Enforce that the userId is passed - noticeState$ can return null * remove strict types from `CopyCipherFieldService` - refactor title to be a string rather than null * remove strict types from `PasswordRepromptComponent` - add guard to exit early on submit but also solves removing null/undefined from typing * use bang to ensure required input * remove strict types from `CopyCipherFieldDirective` - add bang for required types - add default values for null types * add bang for constant variables in cipher form stories * remove strict types from `DeleteAttachmentComponent` - add bang for required types - refactor title to be an empty string * fix tests
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { importProvidersFrom } from "@angular/core";
|
import { importProvidersFrom } from "@angular/core";
|
||||||
import { action } from "@storybook/addon-actions";
|
import { action } from "@storybook/addon-actions";
|
||||||
import {
|
import {
|
||||||
@@ -234,7 +232,7 @@ export const Edit: Story = {
|
|||||||
config: {
|
config: {
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
mode: "edit",
|
mode: "edit",
|
||||||
originalCipher: defaultConfig.originalCipher,
|
originalCipher: defaultConfig.originalCipher!,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -245,7 +243,7 @@ export const PartialEdit: Story = {
|
|||||||
config: {
|
config: {
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
mode: "partial-edit",
|
mode: "partial-edit",
|
||||||
originalCipher: defaultConfig.originalCipher,
|
originalCipher: defaultConfig.originalCipher!,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -256,7 +254,7 @@ export const Clone: Story = {
|
|||||||
config: {
|
config: {
|
||||||
...defaultConfig,
|
...defaultConfig,
|
||||||
mode: "clone",
|
mode: "clone",
|
||||||
originalCipher: defaultConfig.originalCipher,
|
originalCipher: defaultConfig.originalCipher!,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -269,7 +267,7 @@ export const NoPersonalOwnership: Story = {
|
|||||||
mode: "add",
|
mode: "add",
|
||||||
allowPersonalOwnership: false,
|
allowPersonalOwnership: false,
|
||||||
originalCipher: defaultConfig.originalCipher,
|
originalCipher: defaultConfig.originalCipher,
|
||||||
organizations: defaultConfig.organizations,
|
organizations: defaultConfig.organizations!,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ describe("DeleteAttachmentComponent", () => {
|
|||||||
|
|
||||||
expect(showToast).toHaveBeenCalledWith({
|
expect(showToast).toHaveBeenCalledWith({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: null,
|
title: "",
|
||||||
message: "deletedAttachment",
|
message: "deletedAttachment",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
|
|
||||||
@@ -24,10 +22,10 @@ import {
|
|||||||
})
|
})
|
||||||
export class DeleteAttachmentComponent {
|
export class DeleteAttachmentComponent {
|
||||||
/** Id of the cipher associated with the attachment */
|
/** Id of the cipher associated with the attachment */
|
||||||
@Input({ required: true }) cipherId: string;
|
@Input({ required: true }) cipherId!: string;
|
||||||
|
|
||||||
/** The attachment that is can be deleted */
|
/** The attachment that is can be deleted */
|
||||||
@Input({ required: true }) attachment: AttachmentView;
|
@Input({ required: true }) attachment!: AttachmentView;
|
||||||
|
|
||||||
/** Emits when the attachment is successfully deleted */
|
/** Emits when the attachment is successfully deleted */
|
||||||
@Output() onDeletionSuccess = new EventEmitter<void>();
|
@Output() onDeletionSuccess = new EventEmitter<void>();
|
||||||
@@ -56,7 +54,7 @@ export class DeleteAttachmentComponent {
|
|||||||
|
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: null,
|
title: "",
|
||||||
message: this.i18nService.t("deletedAttachment"),
|
message: this.i18nService.t("deletedAttachment"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Directive, HostBinding, HostListener, Input, OnChanges, Optional } from "@angular/core";
|
import { Directive, HostBinding, HostListener, Input, OnChanges, Optional } from "@angular/core";
|
||||||
|
|
||||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||||
@@ -28,9 +26,9 @@ export class CopyCipherFieldDirective implements OnChanges {
|
|||||||
alias: "appCopyField",
|
alias: "appCopyField",
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
action: Exclude<CopyAction, "hiddenField">;
|
action!: Exclude<CopyAction, "hiddenField">;
|
||||||
|
|
||||||
@Input({ required: true }) cipher: CipherView;
|
@Input({ required: true }) cipher!: CipherView;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private copyCipherFieldService: CopyCipherFieldService,
|
private copyCipherFieldService: CopyCipherFieldService,
|
||||||
@@ -52,7 +50,7 @@ export class CopyCipherFieldDirective implements OnChanges {
|
|||||||
@HostListener("click")
|
@HostListener("click")
|
||||||
async copy() {
|
async copy() {
|
||||||
const value = this.getValueToCopy();
|
const value = this.getValueToCopy();
|
||||||
await this.copyCipherFieldService.copy(value, this.action, this.cipher);
|
await this.copyCipherFieldService.copy(value ?? "", this.action, this.cipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnChanges() {
|
async ngOnChanges() {
|
||||||
@@ -69,7 +67,7 @@ export class CopyCipherFieldDirective implements OnChanges {
|
|||||||
|
|
||||||
// If the directive is used on a menu item, update the menu item to prevent keyboard navigation
|
// If the directive is used on a menu item, update the menu item to prevent keyboard navigation
|
||||||
if (this.menuItemDirective) {
|
if (this.menuItemDirective) {
|
||||||
this.menuItemDirective.disabled = this.disabled;
|
this.menuItemDirective.disabled = this.disabled ?? false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Directive, ElementRef, HostBinding, Input, Renderer2 } from "@angular/core";
|
import { Directive, ElementRef, HostBinding, Input, Renderer2 } from "@angular/core";
|
||||||
|
|
||||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
@@ -11,7 +9,7 @@ export type OrgIconSize = "default" | "small" | "large";
|
|||||||
selector: "[appOrgIcon]",
|
selector: "[appOrgIcon]",
|
||||||
})
|
})
|
||||||
export class OrgIconDirective {
|
export class OrgIconDirective {
|
||||||
@Input({ required: true }) tierType: ProductTierType;
|
@Input({ required: true }) tierType!: ProductTierType;
|
||||||
@Input() size?: OrgIconSize = "default";
|
@Input() size?: OrgIconSize = "default";
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { DialogRef } from "@angular/cdk/dialog";
|
import { DialogRef } from "@angular/cdk/dialog";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
import { FormBuilder, ReactiveFormsModule, Validators } from "@angular/forms";
|
||||||
@@ -51,6 +49,12 @@ export class PasswordRepromptComponent {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
submit = async () => {
|
submit = async () => {
|
||||||
|
// Exit early when a master password is not provided.
|
||||||
|
// The form field required error will be shown to users in these cases.
|
||||||
|
if (!this.formGroup.value.masterPassword) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
|
||||||
|
|
||||||
if (userId == null) {
|
if (userId == null) {
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ describe("CopyCipherFieldService", () => {
|
|||||||
expect(toastService.showToast).toHaveBeenCalledWith({
|
expect(toastService.showToast).toHaveBeenCalledWith({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
message: "Username copied",
|
message: "Username copied",
|
||||||
title: null,
|
title: "",
|
||||||
});
|
});
|
||||||
expect(i18nService.t).toHaveBeenCalledWith("username");
|
expect(i18nService.t).toHaveBeenCalledWith("username");
|
||||||
expect(i18nService.t).toHaveBeenCalledWith("valueCopied", "Username");
|
expect(i18nService.t).toHaveBeenCalledWith("valueCopied", "Username");
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
@@ -131,7 +129,7 @@ export class CopyCipherFieldService {
|
|||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
message: this.i18nService.t("valueCopied", this.i18nService.t(action.typeI18nKey)),
|
message: this.i18nService.t("valueCopied", this.i18nService.t(action.typeI18nKey)),
|
||||||
title: null,
|
title: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
if (action.event !== undefined) {
|
if (action.event !== undefined) {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Injectable } from "@angular/core";
|
import { Injectable } from "@angular/core";
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
@@ -17,8 +15,8 @@ import { UserId } from "@bitwarden/common/types/guid";
|
|||||||
// If a user dismisses the notice, use "last_dismissal" to wait 7 days before re-prompting
|
// If a user dismisses the notice, use "last_dismissal" to wait 7 days before re-prompting
|
||||||
// permanent_dismissal will be checked if the user should never see the notice again
|
// permanent_dismissal will be checked if the user should never see the notice again
|
||||||
export class NewDeviceVerificationNotice {
|
export class NewDeviceVerificationNotice {
|
||||||
last_dismissal: Date;
|
last_dismissal: Date | null = null;
|
||||||
permanent_dismissal: boolean;
|
permanent_dismissal: boolean | null = null;
|
||||||
|
|
||||||
constructor(obj: Partial<NewDeviceVerificationNotice>) {
|
constructor(obj: Partial<NewDeviceVerificationNotice>) {
|
||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
@@ -52,12 +50,12 @@ export class NewDeviceVerificationNoticeService {
|
|||||||
return this.stateProvider.getUser(userId, NEW_DEVICE_VERIFICATION_NOTICE_KEY);
|
return this.stateProvider.getUser(userId, NEW_DEVICE_VERIFICATION_NOTICE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
noticeState$(userId: UserId): Observable<NewDeviceVerificationNotice> {
|
noticeState$(userId: UserId): Observable<NewDeviceVerificationNotice | null> {
|
||||||
return this.noticeState(userId).state$;
|
return this.noticeState(userId).state$;
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateNewDeviceVerificationNoticeState(
|
async updateNewDeviceVerificationNoticeState(
|
||||||
userId: UserId | null,
|
userId: UserId,
|
||||||
newState: NewDeviceVerificationNotice,
|
newState: NewDeviceVerificationNotice,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.noticeState(userId).update(() => {
|
await this.noticeState(userId).update(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user