diff --git a/apps/web/src/app/admin-console/organizations/core/services/group/group.service.ts b/apps/web/src/app/admin-console/organizations/core/services/group/group-api.service.ts similarity index 78% rename from apps/web/src/app/admin-console/organizations/core/services/group/group.service.ts rename to apps/web/src/app/admin-console/organizations/core/services/group/group-api.service.ts index e06a9aa8dc7..3b933ab9854 100644 --- a/apps/web/src/app/admin-console/organizations/core/services/group/group.service.ts +++ b/apps/web/src/app/admin-console/organizations/core/services/group/group-api.service.ts @@ -6,8 +6,10 @@ import { ListResponse } from "@bitwarden/common/models/response/list.response"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { CoreOrganizationModule } from "../../core-organization.module"; +import { GroupDetailsView } from "../../views/group-details.view"; import { GroupView } from "../../views/group.view"; +import { AddEditGroupDetail } from "./../../views/add-edit-group-detail"; import { GroupRequest } from "./requests/group.request"; import { OrganizationGroupBulkRequest } from "./requests/organization-group-bulk.request"; import { GroupDetailsResponse, GroupResponse } from "./responses/group.response"; @@ -15,13 +17,13 @@ import { GroupDetailsResponse, GroupResponse } from "./responses/group.response" @Injectable({ providedIn: "root", }) -export class GroupService { +export class GroupApiService { constructor( protected apiService: ApiService, protected configService: ConfigService, ) {} - async get(orgId: string, groupId: string): Promise { + async get(orgId: string, groupId: string): Promise { const r = await this.apiService.send( "GET", "/organizations/" + orgId + "/groups/" + groupId + "/details", @@ -30,7 +32,7 @@ export class GroupService { true, ); - return GroupView.fromResponse(new GroupDetailsResponse(r)); + return GroupDetailsView.fromResponse(new GroupDetailsResponse(r)); } async getAll(orgId: string): Promise { @@ -44,12 +46,26 @@ export class GroupService { const listResponse = new ListResponse(r, GroupDetailsResponse); - return Promise.all(listResponse.data?.map((gr) => GroupView.fromResponse(gr))) ?? []; + return listResponse.data.map((gr) => GroupView.fromResponse(gr)); + } + + async getAllDetails(orgId: string): Promise { + const r = await this.apiService.send( + "GET", + "/organizations/" + orgId + "/groups/details", + null, + true, + true, + ); + + const listResponse = new ListResponse(r, GroupDetailsResponse); + + return listResponse.data.map((gr) => GroupDetailsView.fromResponse(gr)); } } @Injectable({ providedIn: CoreOrganizationModule }) -export class InternalGroupService extends GroupService { +export class InternalGroupApiService extends GroupApiService { constructor( protected apiService: ApiService, protected configService: ConfigService, @@ -77,7 +93,7 @@ export class InternalGroupService extends GroupService { ); } - async save(group: GroupView): Promise { + async save(group: AddEditGroupDetail): Promise { const request = new GroupRequest(); request.name = group.name; request.users = group.members; diff --git a/apps/web/src/app/admin-console/organizations/core/services/index.ts b/apps/web/src/app/admin-console/organizations/core/services/index.ts index 627cb2416ae..88cd6f8763c 100644 --- a/apps/web/src/app/admin-console/organizations/core/services/index.ts +++ b/apps/web/src/app/admin-console/organizations/core/services/index.ts @@ -1,2 +1,2 @@ -export * from "./group/group.service"; +export * from "./group/group-api.service"; export * from "./user-admin.service"; diff --git a/apps/web/src/app/admin-console/organizations/core/views/add-edit-group-detail.ts b/apps/web/src/app/admin-console/organizations/core/views/add-edit-group-detail.ts new file mode 100644 index 00000000000..83fe65c07a9 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/core/views/add-edit-group-detail.ts @@ -0,0 +1,10 @@ +import { CollectionAccessSelectionView } from "@bitwarden/admin-console/common"; + +export interface AddEditGroupDetail { + id: string; + organizationId: string; + name: string; + externalId: string; + collections: CollectionAccessSelectionView[]; + members: string[]; +} diff --git a/apps/web/src/app/admin-console/organizations/core/views/group-details.view.ts b/apps/web/src/app/admin-console/organizations/core/views/group-details.view.ts new file mode 100644 index 00000000000..efa6b9daf79 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/core/views/group-details.view.ts @@ -0,0 +1,20 @@ +import { CollectionAccessSelectionView } from "@bitwarden/admin-console/common"; +import { View } from "@bitwarden/common/models/view/view"; + +import { GroupDetailsResponse } from "../services/group/responses/group.response"; + +export class GroupDetailsView implements View { + id: string; + organizationId: string; + name: string; + externalId: string; + collections: CollectionAccessSelectionView[] = []; + + static fromResponse(response: GroupDetailsResponse): GroupDetailsView { + const view: GroupDetailsView = Object.assign(new GroupDetailsView(), response); + + view.collections = response.collections.map((c) => new CollectionAccessSelectionView(c)); + + return view; + } +} diff --git a/apps/web/src/app/admin-console/organizations/core/views/group.view.ts b/apps/web/src/app/admin-console/organizations/core/views/group.view.ts index 2566e4c4bfa..10ec61142ce 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/group.view.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/group.view.ts @@ -1,23 +1,14 @@ -import { CollectionAccessSelectionView } from "@bitwarden/admin-console/common"; import { View } from "@bitwarden/common/models/view/view"; -import { GroupDetailsResponse, GroupResponse } from "../services/group/responses/group.response"; +import { GroupResponse } from "../services/group/responses/group.response"; export class GroupView implements View { id: string; organizationId: string; name: string; externalId: string; - collections: CollectionAccessSelectionView[] = []; - members: string[] = []; static fromResponse(response: GroupResponse): GroupView { - const view: GroupView = Object.assign(new GroupView(), response) as GroupView; - - if (response instanceof GroupDetailsResponse && response.collections != undefined) { - view.collections = response.collections.map((c) => new CollectionAccessSelectionView(c)); - } - - return view; + return Object.assign(new GroupView(), response); } } diff --git a/apps/web/src/app/admin-console/organizations/core/views/index.ts b/apps/web/src/app/admin-console/organizations/core/views/index.ts index 9408d7757c3..847b2271766 100644 --- a/apps/web/src/app/admin-console/organizations/core/views/index.ts +++ b/apps/web/src/app/admin-console/organizations/core/views/index.ts @@ -1,3 +1,4 @@ export * from "./group.view"; +export * from "./group-details.view"; export * from "./organization-user.view"; export * from "./organization-user-admin-view"; diff --git a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts index 643e76e4c38..c16b2e57241 100644 --- a/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/group-add-edit.component.ts @@ -30,7 +30,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { UserId } from "@bitwarden/common/types/guid"; import { DialogService, ToastService } from "@bitwarden/components"; -import { InternalGroupService as GroupService, GroupView } from "../core"; +import { InternalGroupApiService as GroupService } from "../core"; import { AccessItemType, AccessItemValue, @@ -40,6 +40,8 @@ import { PermissionMode, } from "../shared/components/access-selector"; +import { AddEditGroupDetail } from "./../core/views/add-edit-group-detail"; + /** * Indices for the available tabs in the dialog */ @@ -105,7 +107,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { title: string; collections: AccessItemView[] = []; members: Array = []; - group: GroupView; + group: AddEditGroupDetail; groupForm = this.formBuilder.group({ name: ["", [Validators.required, Validators.maxLength(100)]], @@ -149,7 +151,7 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { ); } - private groupDetails$: Observable = of(this.editMode).pipe( + private groupDetails$: Observable = of(this.editMode).pipe( concatMap((editMode) => { if (!editMode) { return of(undefined); @@ -159,9 +161,11 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { this.groupService.get(this.organizationId, this.groupId), this.apiService.getGroupUsers(this.organizationId, this.groupId), ]).pipe( - map(([groupView, users]) => { - groupView.members = users; - return groupView; + map(([groupView, users]): AddEditGroupDetail => { + return { + ...groupView, + members: users, + }; }), catchError((e: unknown) => { if (e instanceof ErrorResponse) { @@ -295,14 +299,16 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { return; } - const groupView = new GroupView(); - groupView.id = this.groupId; - groupView.organizationId = this.organizationId; - const formValue = this.groupForm.value; - groupView.name = formValue.name; - groupView.members = formValue.members?.map((m) => m.id) ?? []; - groupView.collections = formValue.collections.map((c) => convertToSelectionView(c)); + + const groupView: AddEditGroupDetail = { + id: this.groupId, + organizationId: this.organizationId, + name: formValue.name, + members: formValue.members?.map((m) => m.id) ?? [], + collections: formValue.collections.map((c) => convertToSelectionView(c)), + externalId: formValue.externalId, + }; await this.groupService.save(groupView); @@ -346,7 +352,10 @@ export class GroupAddEditComponent implements OnInit, OnDestroy { /** * Maps the group's current collection access to AccessItemValues to populate the access-selector's FormControl */ -function mapToAccessSelections(group: GroupView, items: AccessItemView[]): AccessItemValue[] { +function mapToAccessSelections( + group: AddEditGroupDetail, + items: AccessItemView[], +): AccessItemValue[] { return ( group.collections // The FormControl value only represents editable collection access - exclude readonly access selections @@ -365,7 +374,7 @@ function mapToAccessSelections(group: GroupView, items: AccessItemView[]): Acces function mapToAccessItemViews( collections: CollectionAdminView[], organization: Organization, - group?: GroupView, + group?: AddEditGroupDetail, ): AccessItemView[] { return ( collections diff --git a/apps/web/src/app/admin-console/organizations/manage/groups.component.ts b/apps/web/src/app/admin-console/organizations/manage/groups.component.ts index 7d660682a2d..dd0cde70e67 100644 --- a/apps/web/src/app/admin-console/organizations/manage/groups.component.ts +++ b/apps/web/src/app/admin-console/organizations/manage/groups.component.ts @@ -28,7 +28,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { DialogService, TableDataSource, ToastService } from "@bitwarden/components"; -import { InternalGroupService as GroupService, GroupView } from "../core"; +import { GroupDetailsView, InternalGroupApiService as GroupService } from "../core"; import { GroupAddEditDialogResultType, @@ -40,7 +40,7 @@ type GroupDetailsRow = { /** * Details used for displaying group information */ - details: GroupView; + details: GroupDetailsView; /** * True if the group is selected in the table @@ -108,7 +108,7 @@ export class GroupsComponent { ), // groups this.refreshGroups$.pipe( - switchMap(() => this.groupService.getAll(this.organizationId)), + switchMap(() => this.groupService.getAllDetails(this.organizationId)), ), ]), ), diff --git a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts index 75c943fe579..04df2957f91 100644 --- a/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/members/components/member-dialog/member-dialog.component.ts @@ -35,8 +35,8 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { DialogService, ToastService } from "@bitwarden/components"; import { - GroupService, - GroupView, + GroupApiService, + GroupDetailsView, OrganizationUserAdminView, UserAdminService, } from "../../../core"; @@ -144,7 +144,7 @@ export class MemberDialogComponent implements OnDestroy { private formBuilder: FormBuilder, // TODO: We should really look into consolidating naming conventions for these services private collectionAdminService: CollectionAdminService, - private groupService: GroupService, + private groupService: GroupApiService, private userService: UserAdminService, private organizationUserApiService: OrganizationUserApiService, private dialogService: DialogService, @@ -171,8 +171,8 @@ export class MemberDialogComponent implements OnDestroy { const groups$ = this.organization$.pipe( switchMap((organization) => organization.useGroups - ? this.groupService.getAll(this.params.organizationId) - : of([] as GroupView[]), + ? this.groupService.getAllDetails(this.params.organizationId) + : of([] as GroupDetailsView[]), ), ); @@ -278,7 +278,7 @@ export class MemberDialogComponent implements OnDestroy { private loadOrganizationUser( userDetails: OrganizationUserAdminView, - groups: GroupView[], + groups: GroupDetailsView[], collections: CollectionAdminView[], organization: Organization, ) { @@ -635,7 +635,7 @@ function mapCollectionToAccessItemView( collection: CollectionAdminView, organization: Organization, accessSelection?: CollectionAccessSelectionView, - group?: GroupView, + group?: GroupDetailsView, ): AccessItemView { return { type: AccessItemType.Collection, @@ -648,7 +648,7 @@ function mapCollectionToAccessItemView( }; } -function mapGroupToAccessItemView(group: GroupView): AccessItemView { +function mapGroupToAccessItemView(group: GroupDetailsView): AccessItemView { return { type: AccessItemType.Group, id: group.id, 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 f49fb13d6c8..f1cd505de0a 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 @@ -56,7 +56,7 @@ import { } from "../../../billing/organizations/change-plan-dialog.component"; import { BaseMembersComponent } from "../../common/base-members.component"; import { PeopleTableDataSource } from "../../common/people-table-data-source"; -import { GroupService } from "../core"; +import { GroupApiService } from "../core"; import { OrganizationUserView } from "../core/views/organization-user.view"; import { openEntityEventsDialog } from "../manage/entity-events.component"; @@ -129,7 +129,7 @@ export class MembersComponent extends BaseMembersComponent private organizationApiService: OrganizationApiServiceAbstraction, private organizationUserApiService: OrganizationUserApiService, private router: Router, - private groupService: GroupService, + private groupService: GroupApiService, private collectionService: CollectionService, private billingApiService: BillingApiServiceAbstraction, private modalService: ModalService, diff --git a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts index a51d408bc74..514d0f8b625 100644 --- a/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts +++ b/apps/web/src/app/vault/components/collection-dialog/collection-dialog.component.ts @@ -29,7 +29,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl import { Utils } from "@bitwarden/common/platform/misc/utils"; import { BitValidators, DialogService } from "@bitwarden/components"; -import { GroupService, GroupView } from "../../../admin-console/organizations/core"; +import { GroupApiService, GroupView } from "../../../admin-console/organizations/core"; import { PermissionMode } from "../../../admin-console/organizations/shared/components/access-selector/access-selector.component"; import { AccessItemType, @@ -101,7 +101,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy { private formBuilder: FormBuilder, private dialogRef: DialogRef, private organizationService: OrganizationService, - private groupService: GroupService, + private groupService: GroupApiService, private collectionAdminService: CollectionAdminService, private i18nService: I18nService, private platformUtilsService: PlatformUtilsService, diff --git a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts b/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts index c8c9b83efcb..f86a321dfbd 100644 --- a/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts +++ b/apps/web/src/app/vault/org-vault/bulk-collections-dialog/bulk-collections-dialog.component.ts @@ -14,7 +14,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { DialogService } from "@bitwarden/components"; -import { GroupService, GroupView } from "../../../admin-console/organizations/core"; +import { GroupApiService, GroupView } from "../../../admin-console/organizations/core"; import { AccessItemType, AccessItemValue, @@ -61,7 +61,7 @@ export class BulkCollectionsDialogComponent implements OnDestroy { private dialogRef: DialogRef, private formBuilder: FormBuilder, private organizationService: OrganizationService, - private groupService: GroupService, + private groupService: GroupApiService, private organizationUserApiService: OrganizationUserApiService, private platformUtilsService: PlatformUtilsService, private i18nService: I18nService, diff --git a/apps/web/src/app/vault/org-vault/vault.component.ts b/apps/web/src/app/vault/org-vault/vault.component.ts index 743e080befe..dee584f3a42 100644 --- a/apps/web/src/app/vault/org-vault/vault.component.ts +++ b/apps/web/src/app/vault/org-vault/vault.component.ts @@ -81,7 +81,7 @@ import { PasswordRepromptService, } from "@bitwarden/vault"; -import { GroupService, GroupView } from "../../admin-console/organizations/core"; +import { GroupApiService, GroupView } from "../../admin-console/organizations/core"; import { openEntityEventsDialog } from "../../admin-console/organizations/manage/entity-events.component"; import { TrialFlowService } from "../../billing/services/trial-flow.service"; import { FreeTrial } from "../../core/types/free-trial"; @@ -234,7 +234,7 @@ export class VaultComponent implements OnInit, OnDestroy { private collectionAdminService: CollectionAdminService, private searchService: SearchService, private searchPipe: SearchPipe, - private groupService: GroupService, + private groupService: GroupApiService, private logService: LogService, private eventCollectionService: EventCollectionService, private totpService: TotpService,