diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts index 697fe6540c..c8a5668d7b 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/overview/overview.component.ts @@ -254,10 +254,10 @@ export class OverviewComponent implements OnInit, OnDestroy { }); } - openDeleteSecret(secretIds: string[]) { + openDeleteSecret(event: SecretListView[]) { this.dialogService.open(SecretDeleteDialogComponent, { data: { - secretIds: secretIds, + secrets: event, }, }); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts index b2f9212931..01d7e158d5 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/projects/project/project-secrets.component.ts @@ -62,10 +62,10 @@ export class ProjectSecretsComponent { }); } - openDeleteSecret(secretIds: string[]) { + openDeleteSecret(event: SecretListView[]) { this.dialogService.open(SecretDeleteDialogComponent, { data: { - secretIds: secretIds, + secrets: event, }, }); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.html index aba920d04f..95b34440ba 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.html @@ -1,7 +1,7 @@ {{ title | i18n }} -
+
{{ "softDeleteSecretWarning" | i18n }}
{{ "deleteItemConfirmation" | i18n }} diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.ts index 1bd8de2053..f8d9b03c8a 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-delete.component.ts @@ -3,11 +3,18 @@ import { Component, Inject } from "@angular/core"; import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service"; +import { DialogService } from "@bitwarden/components"; +import { SecretListView } from "../../models/view/secret-list.view"; +import { + BulkOperationStatus, + BulkStatusDetails, + BulkStatusDialogComponent, +} from "../../shared/dialogs/bulk-status-dialog.component"; import { SecretService } from "../secret.service"; export interface SecretDeleteOperation { - secretIds: string[]; + secrets: SecretListView[]; } @Component({ @@ -20,22 +27,45 @@ export class SecretDeleteDialogComponent { private secretService: SecretService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, - @Inject(DIALOG_DATA) public data: SecretDeleteOperation + @Inject(DIALOG_DATA) private data: SecretDeleteOperation, + private dialogService: DialogService ) {} + showSoftDeleteSecretWarning = this.data.secrets.length === 1; + get title() { - return this.data.secretIds.length === 1 ? "deleteSecret" : "deleteSecrets"; + return this.data.secrets.length === 1 ? "deleteSecret" : "deleteSecrets"; } get submitButtonText() { - return this.data.secretIds.length === 1 ? "deleteSecret" : "deleteSecrets"; + return this.data.secrets.length === 1 ? "deleteSecret" : "deleteSecrets"; } delete = async () => { - await this.secretService.delete(this.data.secretIds); + const bulkResponses = await this.secretService.delete(this.data.secrets); + + if (bulkResponses.find((response) => response.errorMessage)) { + this.openBulkStatusDialog(bulkResponses.filter((response) => response.errorMessage)); + this.dialogRef.close(); + return; + } + const message = - this.data.secretIds.length === 1 ? "softDeleteSuccessToast" : "softDeletesSuccessToast"; - this.dialogRef.close(this.data.secretIds); + this.data.secrets.length === 1 ? "softDeleteSuccessToast" : "softDeletesSuccessToast"; this.platformUtilsService.showToast("success", null, this.i18nService.t(message)); + + this.dialogRef.close(); }; + + openBulkStatusDialog(bulkStatusResults: BulkOperationStatus[]) { + this.dialogService.open(BulkStatusDialogComponent, { + data: { + title: "deleteSecrets", + subTitle: "secrets", + columnTitle: "secretName", + message: "bulkDeleteSecretsErrorMessage", + details: bulkStatusResults, + }, + }); + } } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts index 937f3c113b..7459a534a7 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts @@ -8,6 +8,8 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { DialogService } from "@bitwarden/components"; import { ProjectListView } from "../../models/view/project-list.view"; +import { SecretListView } from "../../models/view/secret-list.view"; +import { SecretProjectView } from "../../models/view/secret-project.view"; import { SecretView } from "../../models/view/secret.view"; import { ProjectService } from "../../projects/project.service"; import { SecretService } from "../secret.service"; @@ -112,11 +114,13 @@ export class SecretDialogComponent implements OnInit { } protected openDeleteSecretDialog() { + const secretListView: SecretListView[] = this.getSecretListView(); + const dialogRef = this.dialogService.open( SecretDeleteDialogComponent, { data: { - secretIds: [this.data.secretId], + secrets: secretListView, }, } ); @@ -146,4 +150,17 @@ export class SecretDialogComponent implements OnInit { secretView.projects = [this.projects.find((p) => p.id == this.formGroup.value.project)]; return secretView; } + + private getSecretListView() { + const secretListViews: SecretListView[] = []; + const emptyProjects: SecretProjectView[] = []; + const selectedProject = [this.projects.find((p) => p.id == this.formGroup.value.project)]; + + const secretListView = new SecretListView(); + secretListView.organizationId = this.data.organizationId; + secretListView.name = this.formGroup.value.name; + secretListView.projects = selectedProject ? selectedProject : emptyProjects; + secretListViews.push(secretListView); + return secretListViews; + } } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts index e88f535ec6..107bd9a736 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secret.service.ts @@ -10,6 +10,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-cr import { SecretListView } from "../models/view/secret-list.view"; import { SecretProjectView } from "../models/view/secret-project.view"; import { SecretView } from "../models/view/secret.view"; +import { BulkOperationStatus } from "../shared/dialogs/bulk-status-dialog.component"; import { SecretRequest } from "./requests/secret.request"; import { SecretListItemResponse } from "./responses/secret-list-item.response"; @@ -82,23 +83,18 @@ export class SecretService { this._secret.next(await this.createSecretView(new SecretResponse(r))); } - async delete(secretIds: string[]) { + async delete(secrets: SecretListView[]): Promise { + const secretIds = secrets.map((secret) => secret.id); const r = await this.apiService.send("POST", "/secrets/delete", secretIds, true, true); - const responseErrors: string[] = []; - r.data.forEach((element: { error: string }) => { - if (element.error) { - responseErrors.push(element.error); - } - }); - - // TODO waiting to hear back on how to display multiple errors. - // for now send as a list of strings to be displayed in toast. - if (responseErrors?.length >= 1) { - throw new Error(responseErrors.join(",")); - } - this._secret.next(null); + return r.data.map((element: { id: string; error: string }) => { + const bulkOperationStatus = new BulkOperationStatus(); + bulkOperationStatus.id = element.id; + bulkOperationStatus.name = secrets.find((secret) => secret.id == element.id).name; + bulkOperationStatus.errorMessage = element.error; + return bulkOperationStatus; + }); } async getTrashedSecrets(organizationId: string): Promise { diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts index d590826204..938e168188 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts +++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/secrets.component.ts @@ -66,10 +66,10 @@ export class SecretsComponent implements OnInit { }); } - openDeleteSecret(secretIds: string[]) { + openDeleteSecret(event: SecretListView[]) { this.dialogService.open(SecretDeleteDialogComponent, { data: { - secretIds: secretIds, + secrets: event, }, }); } diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html index 52a5c2255c..829e23e812 100644 --- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html +++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/secrets-list.component.html @@ -124,7 +124,7 @@ {{ "restoreSecret" | i18n }} -