1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-08 20:50:28 +00:00

PM-23602 Collection name in event logs

This commit is contained in:
voommen-livefront
2025-07-11 08:56:11 -05:00
parent 88e4afb003
commit f79a522791
2 changed files with 59 additions and 6 deletions

View File

@@ -1,6 +1,8 @@
import { mock } from "jest-mock-extended";
import {
CollectionService,
CollectionView,
OrganizationUserApiService,
OrganizationUserUserMiniResponse,
} from "@bitwarden/admin-console/common";
@@ -24,9 +26,11 @@ describe("EventService", () => {
let mockAccountService: AccountService;
let mockCipherService = mock<CipherService>();
let mockOrgUserApiService = mock<OrganizationUserApiService>();
let mockCollectionService = mock<CollectionService>();
const userId = Utils.newGuid() as UserId;
const orgId = Utils.newGuid() as OrganizationId;
const collectionId = Utils.newGuid() as string;
const orgUserMiniResponse: ListResponse<OrganizationUserUserMiniResponse> = {
continuationToken: null,
@@ -39,12 +43,20 @@ describe("EventService", () => {
],
} as ListResponse<OrganizationUserUserMiniResponse>;
const collectionViews = [
{
id: collectionId,
name: "Test Collection",
} as CollectionView,
];
const baseEvent = {
type: EventType.Cipher_Created,
cipherId: "abcdef1234567890",
organizationId: "orgid1234567890" as OrganizationId,
organizationUserId: userId,
userId: userId,
collectionId: collectionId,
deviceType: 0, // Android
} as any;
@@ -54,14 +66,19 @@ describe("EventService", () => {
mockAccountService = mockAccountServiceWith(userId);
mockCipherService = mock<CipherService>();
mockOrgUserApiService = mock<OrganizationUserApiService>();
mockCollectionService = mock<CollectionService>();
eventService = new EventService(
mocki18nService,
mockPolicyService,
mockAccountService,
mockCipherService,
mockOrgUserApiService,
mockCollectionService,
);
// reset mocks
jest.clearAllMocks();
// Default mock for i18nService.t
mocki18nService.t.mockImplementation((key: string, value?: string) => {
if (value) {
@@ -70,12 +87,11 @@ describe("EventService", () => {
return key;
});
// mock response for getAllDecrypted
mockCipherService.getAllDecrypted.mockResolvedValue([
{ id: "abcdef1234567890", name: "Test Cipher" } as CipherView,
]);
// mock response for getAllMiniUserDetails
mockOrgUserApiService.getAllMiniUserDetails.mockResolvedValue(orgUserMiniResponse);
mockCollectionService.getAllDecrypted.mockResolvedValue(collectionViews);
// this method will use the mocks defined above
await eventService.loadAllOrganizationInfo(orgId, userId);
@@ -241,4 +257,27 @@ describe("EventService", () => {
});
});
});
describe("getEventInfo for Collections", () => {
const testCases = [
{ type: EventType.Collection_Created, eventName: "createdCollectionId" },
{ type: EventType.Collection_Updated, eventName: "editedCollectionId" },
{ type: EventType.Collection_Deleted, eventName: "deletedCollectionId" },
];
testCases.forEach(({ type, eventName }) => {
it(`should return correct info for collection event type ${type}`, async () => {
const event = { ...baseEvent, type };
const info = await eventService.getEventInfo(event);
expect(info.message).toContain("Test Collection");
expect(info.humanReadableMessage).toContain(eventName);
expect(info.appIcon).toBe("bwi-mobile");
expect(info.appName).toBe("mobile - Android");
expect(info.eventName).toBe(eventName);
expect(info.eventLink).toContain("<code>Test Collection</code>");
});
});
});
});

View File

@@ -1,7 +1,7 @@
import { Injectable } from "@angular/core";
import { switchMap } from "rxjs";
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
import { CollectionService, OrganizationUserApiService } from "@bitwarden/admin-console/common";
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
@@ -19,6 +19,7 @@ export class EventService {
private policies: Policy[] = [];
private ciphers: CipherView[] = [];
private organizationUserIdMap: Map<string, { name: string; email: string }> = new Map();
private collections: Map<string, { name: string }> = new Map();
constructor(
private i18nService: I18nService,
@@ -26,6 +27,7 @@ export class EventService {
accountService: AccountService,
private cipherService: CipherService,
private organizationUserApiService: OrganizationUserApiService,
private collectionService: CollectionService,
) {
accountService.activeAccount$
.pipe(
@@ -57,10 +59,13 @@ export class EventService {
async loadAllOrganizationInfo(organizationId: OrganizationId, userId: UserId) {
this.ciphers = await this.cipherService.getAllDecrypted(userId);
const orgUsers = await this.organizationUserApiService.getAllMiniUserDetails(organizationId);
orgUsers.data.forEach((u) => {
(await this.organizationUserApiService.getAllMiniUserDetails(organizationId)).data.map((u) => {
this.organizationUserIdMap.set(u.id, { name: u.name, email: u.email });
});
(await this.collectionService.getAllDecrypted()).map((c) => {
this.collections.set(c.id, { name: c.name });
});
}
async getEventInfo(ev: EventResponse, options = new EventOptions()): Promise<EventInfo> {
@@ -270,6 +275,8 @@ export class EventService {
"createdCollectionId",
this.getShortId(ev.collectionId),
);
eventName = this.i18nService.t("createdCollectionId", "");
eventLink = this.formatCollectionId(ev);
break;
case EventType.Collection_Updated:
msg = this.i18nService.t("editedCollectionId", this.formatCollectionId(ev));
@@ -277,6 +284,8 @@ export class EventService {
"editedCollectionId",
this.getShortId(ev.collectionId),
);
eventName = this.i18nService.t("editedCollectionId", "");
eventLink = this.formatCollectionId(ev);
break;
case EventType.Collection_Deleted:
msg = this.i18nService.t("deletedCollectionId", this.formatCollectionId(ev));
@@ -284,6 +293,8 @@ export class EventService {
"deletedCollectionId",
this.getShortId(ev.collectionId),
);
eventName = this.i18nService.t("deletedCollectionId", "");
eventLink = this.formatCollectionId(ev);
break;
// Group
case EventType.Group_Created:
@@ -679,7 +690,10 @@ export class EventService {
private formatCollectionId(ev: EventResponse) {
const shortId = this.getShortId(ev.collectionId);
const a = this.makeAnchor(shortId);
const anchorName = this.collections.get(ev.collectionId)?.name ?? shortId;
const a = this.makeAnchor(anchorName);
a.setAttribute(
"href",
`#/organizations/${ev.organizationId}/vault?collectionId=${ev.collectionId}`,