From 485f62036738458e29e41574d1ff44bba5d84d1a Mon Sep 17 00:00:00 2001 From: Brandon Treston Date: Fri, 13 Feb 2026 10:02:36 -0500 Subject: [PATCH] [PM-32075] Fix self host bug due to type mismatch (#18919) * fix self host bug with data model * fix type issues * fix types, make successful required --- .../members/components/bulk/bulk-status.component.ts | 5 ++--- .../members/deprecated_members.component.ts | 2 +- .../organizations/members/members.component.spec.ts | 4 ++-- .../organizations/members/members.component.ts | 2 +- .../member-actions/member-actions.service.spec.ts | 2 +- .../services/member-actions/member-actions.service.ts | 8 ++------ .../member-dialog-manager.service.ts | 4 +++- .../providers/manage/deprecated_members.component.ts | 10 ++++++---- .../providers/manage/members.component.ts | 10 ++++++---- 9 files changed, 24 insertions(+), 23 deletions(-) diff --git a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-status.component.ts b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-status.component.ts index 5c9bf919ed4..cfddb17627a 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-status.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/bulk/bulk-status.component.ts @@ -9,7 +9,6 @@ import { } from "@bitwarden/common/admin-console/enums"; import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response"; import { ProviderUserUserDetailsResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user.response"; -import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { DIALOG_DATA, DialogConfig, DialogService } from "@bitwarden/components"; @@ -34,7 +33,7 @@ type BulkStatusEntry = { type BulkStatusDialogData = { users: Array; filteredUsers: Array; - request: Promise>; + request: Promise; successfulMessage: string; }; @@ -63,7 +62,7 @@ export class BulkStatusComponent implements OnInit { async showBulkStatus(data: BulkStatusDialogData) { try { const response = await data.request; - const keyedErrors: any = response.data + const keyedErrors: any = (response ?? []) .filter((r) => r.error !== "") .reduce((a, x) => ({ ...a, [x.id]: x.error }), {}); const keyedFilteredUsers: any = data.filteredUsers.reduce( diff --git a/apps/web/src/app/admin-console/organizations/members/deprecated_members.component.ts b/apps/web/src/app/admin-console/organizations/members/deprecated_members.component.ts index dae9bafbcfe..1f1e19e2a6f 100644 --- a/apps/web/src/app/admin-console/organizations/members/deprecated_members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/deprecated_members.component.ts @@ -446,7 +446,7 @@ export class MembersComponent extends BaseMembersComponent try { const result = await this.memberActionsService.bulkReinvite(organization, filteredUsers); - if (!result.successful) { + if (result.successful.length === 0) { throw new Error(); } diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.spec.ts b/apps/web/src/app/admin-console/organizations/members/members.component.spec.ts index 1cd90989b12..9a371de1acd 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.spec.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.spec.ts @@ -515,7 +515,7 @@ describe("vNextMembersComponent", () => { }; jest.spyOn(component["dataSource"](), "isIncreasedBulkLimitEnabled").mockReturnValue(false); jest.spyOn(component["dataSource"](), "getCheckedUsers").mockReturnValue([invitedUser]); - mockMemberActionsService.bulkReinvite.mockResolvedValue({ successful: true }); + mockMemberActionsService.bulkReinvite.mockResolvedValue({ successful: [{}], failed: [] }); await component.bulkReinvite(mockOrg); @@ -549,7 +549,7 @@ describe("vNextMembersComponent", () => { jest.spyOn(component["dataSource"](), "isIncreasedBulkLimitEnabled").mockReturnValue(false); jest.spyOn(component["dataSource"](), "getCheckedUsers").mockReturnValue([invitedUser]); const error = new Error("Bulk reinvite failed"); - mockMemberActionsService.bulkReinvite.mockResolvedValue({ successful: false, failed: error }); + mockMemberActionsService.bulkReinvite.mockResolvedValue({ successful: [], failed: error }); await component.bulkReinvite(mockOrg); diff --git a/apps/web/src/app/admin-console/organizations/members/members.component.ts b/apps/web/src/app/admin-console/organizations/members/members.component.ts index 6139c5f07a5..826bdfb5f69 100644 --- a/apps/web/src/app/admin-console/organizations/members/members.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/members.component.ts @@ -426,7 +426,7 @@ export class vNextMembersComponent { const result = await this.memberActionsService.bulkReinvite(organization, filteredUsers); - if (!result.successful) { + if (result.successful.length === 0) { this.validationService.showError(result.failed); } diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.spec.ts b/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.spec.ts index 688c7ed77ce..1ba056a24f6 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.spec.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.spec.ts @@ -507,7 +507,7 @@ describe("MemberActionsService", () => { const result = await service.bulkReinvite(mockOrganization, users); - expect(result.successful).toBeUndefined(); + expect(result.successful).toHaveLength(0); expect(result.failed).toHaveLength(totalUsers); expect(result.failed.every((f) => f.error === errorMessage)).toBe(true); expect(organizationUserApiService.postManyOrganizationUserReinvite).toHaveBeenCalledTimes(2); diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.ts b/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.ts index e5f8c0c6673..7d573c8eeef 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/member-actions/member-actions.service.ts @@ -37,11 +37,7 @@ export interface MemberActionResult { } export class BulkActionResult { - constructor() { - this.failed = []; - } - - successful?: OrganizationUserBulkResponse[]; + successful: OrganizationUserBulkResponse[] = []; failed: { id: string; error: string }[] = []; } @@ -316,7 +312,7 @@ export class MemberActionsService { } return { - successful: allSuccessful.length > 0 ? allSuccessful : undefined, + successful: allSuccessful, failed: allFailed, }; } diff --git a/apps/web/src/app/admin-console/organizations/members/services/member-dialog-manager/member-dialog-manager.service.ts b/apps/web/src/app/admin-console/organizations/members/services/member-dialog-manager/member-dialog-manager.service.ts index 18106031fd0..6c367692376 100644 --- a/apps/web/src/app/admin-console/organizations/members/services/member-dialog-manager/member-dialog-manager.service.ts +++ b/apps/web/src/app/admin-console/organizations/members/services/member-dialog-manager/member-dialog-manager.service.ts @@ -1,8 +1,10 @@ import { Injectable, WritableSignal } from "@angular/core"; import { firstValueFrom, lastValueFrom } from "rxjs"; +import { OrganizationUserBulkResponse } from "@bitwarden/admin-console/common"; import { UserNamePipe } from "@bitwarden/angular/pipes/user-name.pipe"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { ProviderUserBulkResponse } from "@bitwarden/common/admin-console/models/response/provider/provider-user-bulk.response"; import { ProductTierType } from "@bitwarden/common/billing/enums"; import { OrganizationBillingMetadataResponse } from "@bitwarden/common/billing/models/response/organization-billing-metadata.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -197,7 +199,7 @@ export class MemberDialogManagerService { async openBulkStatusDialog( users: OrganizationUserView[], filteredUsers: OrganizationUserView[], - request: Promise, + request: Promise, successMessage: string, ): Promise { const dialogRef = BulkStatusComponent.open(this.dialogService, { diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/deprecated_members.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/deprecated_members.component.ts index 1b1ae25c027..464b9982689 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/deprecated_members.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/deprecated_members.component.ts @@ -223,10 +223,12 @@ export class MembersComponent extends BaseMembersComponent { } } else { // Feature flag disabled - show legacy dialog - const request = this.apiService.postManyProviderUserReinvite( - this.providerId, - new ProviderUserBulkRequest(checkedInvitedUsers.map((user) => user.id)), - ); + const request = this.apiService + .postManyProviderUserReinvite( + this.providerId, + new ProviderUserBulkRequest(checkedInvitedUsers.map((user) => user.id)), + ) + .then((response) => response.data); const dialogRef = BulkStatusComponent.open(this.dialogService, { data: { diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts index c63bda449c5..308b93ac2e3 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/manage/members.component.ts @@ -236,10 +236,12 @@ export class vNextMembersComponent { } } else { // In self-hosted environments, show legacy dialog - const request = this.apiService.postManyProviderUserReinvite( - providerId, - new ProviderUserBulkRequest(checkedInvitedUsers.map((user) => user.id)), - ); + const request = this.apiService + .postManyProviderUserReinvite( + providerId, + new ProviderUserBulkRequest(checkedInvitedUsers.map((user) => user.id)), + ) + .then((response) => response.data); const dialogRef = BulkStatusComponent.open(this.dialogService, { data: {