1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-10-28 07:49:41 +01:00

Add rxjs operators and update last callers of decryptedCollectionViews$

This commit is contained in:
Thomas Rittson 2024-10-28 12:10:16 +10:00
parent c0b97ec21f
commit 10359328f3
No known key found for this signature in database
GPG Key ID: CDDDA03861C35E27
6 changed files with 103 additions and 44 deletions

View File

@ -0,0 +1 @@
export * from "./rxjs-operators";

View File

@ -0,0 +1,58 @@
import { firstValueFrom, of } from "rxjs";
import { getById, getByIds } from "./rxjs-operators";
describe("custom rxjs operators", () => {
describe("getById", () => {
it("returns an object with a matching id", async () => {
const input = [
{
id: 1,
data: "one",
},
{
id: 2,
data: "two",
},
{
id: 3,
data: "three",
},
];
const output = await firstValueFrom(getById(2)(of(input)));
expect(output).toEqual([{ id: 1, data: "one" }]);
});
});
describe("getByIds", () => {
it("returns an array of objects with matching ids", async () => {
const input = [
{
id: 1,
data: "one",
},
{
id: 2,
data: "two",
},
{
id: 3,
data: "three",
},
{
id: 4,
data: "four",
},
];
const output = await firstValueFrom(getByIds([2, 3])(of(input)));
expect(output).toEqual([
{ id: 2, data: "two" },
{ id: 3, data: "three" },
]);
});
});
});

View File

@ -0,0 +1,9 @@
import { map } from "rxjs";
type ObjectWithId<TId> = { id: TId };
export const getById = <TId, T extends ObjectWithId<TId>>(id: TId) =>
map<T[], T>((objects) => objects.find((o) => o.id === id));
export const getByIds = <TId, T extends ObjectWithId<TId>>(ids: TId[]) =>
map<T[], T[]>((objects) => objects.filter((o) => ids.includes(o.id)));

View File

@ -4,8 +4,9 @@ import { firstValueFrom, of } from "rxjs";
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { CollectionId } from "@bitwarden/common/types/guid"; import { CollectionId, UserId } from "@bitwarden/common/types/guid";
import { mockAccountServiceWith } from "../../../spec";
import { CipherView } from "../models/view/cipher.view"; import { CipherView } from "../models/view/cipher.view";
import { import {
@ -14,10 +15,13 @@ import {
} from "./cipher-authorization.service"; } from "./cipher-authorization.service";
describe("CipherAuthorizationService", () => { describe("CipherAuthorizationService", () => {
const userId = "UserId" as UserId;
let cipherAuthorizationService: CipherAuthorizationService; let cipherAuthorizationService: CipherAuthorizationService;
const mockCollectionService = mock<CollectionService>(); const mockCollectionService = mock<CollectionService>();
const mockOrganizationService = mock<OrganizationService>(); const mockOrganizationService = mock<OrganizationService>();
const mockAccountService = mockAccountServiceWith(userId);
// Mock factories // Mock factories
const createMockCipher = ( const createMockCipher = (
@ -56,6 +60,7 @@ describe("CipherAuthorizationService", () => {
cipherAuthorizationService = new DefaultCipherAuthorizationService( cipherAuthorizationService = new DefaultCipherAuthorizationService(
mockCollectionService, mockCollectionService,
mockOrganizationService, mockOrganizationService,
mockAccountService,
); );
}); });
@ -113,7 +118,7 @@ describe("CipherAuthorizationService", () => {
createMockCollection("col1", true), createMockCollection("col1", true),
createMockCollection("col2", false), createMockCollection("col2", false),
]; ];
mockCollectionService.decryptedCollectionViews$.mockReturnValue( mockCollectionService.decryptedCollections$.mockReturnValue(
of(allCollections as CollectionView[]), of(allCollections as CollectionView[]),
); );
@ -121,10 +126,6 @@ describe("CipherAuthorizationService", () => {
.canDeleteCipher$(cipher, [activeCollectionId]) .canDeleteCipher$(cipher, [activeCollectionId])
.subscribe((result) => { .subscribe((result) => {
expect(result).toBe(true); expect(result).toBe(true);
expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([
"col1",
"col2",
] as CollectionId[]);
done(); done();
}); });
}); });
@ -139,7 +140,7 @@ describe("CipherAuthorizationService", () => {
createMockCollection("col1", false), createMockCollection("col1", false),
createMockCollection("col2", true), createMockCollection("col2", true),
]; ];
mockCollectionService.decryptedCollectionViews$.mockReturnValue( mockCollectionService.decryptedCollections$.mockReturnValue(
of(allCollections as CollectionView[]), of(allCollections as CollectionView[]),
); );
@ -147,10 +148,6 @@ describe("CipherAuthorizationService", () => {
.canDeleteCipher$(cipher, [activeCollectionId]) .canDeleteCipher$(cipher, [activeCollectionId])
.subscribe((result) => { .subscribe((result) => {
expect(result).toBe(false); expect(result).toBe(false);
expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([
"col1",
"col2",
] as CollectionId[]);
done(); done();
}); });
}); });
@ -165,17 +162,12 @@ describe("CipherAuthorizationService", () => {
createMockCollection("col2", true), createMockCollection("col2", true),
createMockCollection("col3", false), createMockCollection("col3", false),
]; ];
mockCollectionService.decryptedCollectionViews$.mockReturnValue( mockCollectionService.decryptedCollections$.mockReturnValue(
of(allCollections as CollectionView[]), of(allCollections as CollectionView[]),
); );
cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => { cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => {
expect(result).toBe(true); expect(result).toBe(true);
expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([
"col1",
"col2",
"col3",
] as CollectionId[]);
done(); done();
}); });
}); });
@ -189,16 +181,12 @@ describe("CipherAuthorizationService", () => {
createMockCollection("col1", false), createMockCollection("col1", false),
createMockCollection("col2", false), createMockCollection("col2", false),
]; ];
mockCollectionService.decryptedCollectionViews$.mockReturnValue( mockCollectionService.decryptedCollections$.mockReturnValue(
of(allCollections as CollectionView[]), of(allCollections as CollectionView[]),
); );
cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => { cipherAuthorizationService.canDeleteCipher$(cipher).subscribe((result) => {
expect(result).toBe(false); expect(result).toBe(false);
expect(mockCollectionService.decryptedCollectionViews$).toHaveBeenCalledWith([
"col1",
"col2",
] as CollectionId[]);
done(); done();
}); });
}); });
@ -246,7 +234,7 @@ describe("CipherAuthorizationService", () => {
createMockCollection("col1", true), createMockCollection("col1", true),
createMockCollection("col2", false), createMockCollection("col2", false),
]; ];
mockCollectionService.decryptedCollectionViews$.mockReturnValue( mockCollectionService.decryptedCollections$.mockReturnValue(
of(allCollections as CollectionView[]), of(allCollections as CollectionView[]),
); );
@ -263,7 +251,7 @@ describe("CipherAuthorizationService", () => {
createMockCollection("col1", false), createMockCollection("col1", false),
createMockCollection("col2", false), createMockCollection("col2", false),
]; ];
mockCollectionService.decryptedCollectionViews$.mockReturnValue( mockCollectionService.decryptedCollections$.mockReturnValue(
of(allCollections as CollectionView[]), of(allCollections as CollectionView[]),
); );

View File

@ -4,6 +4,9 @@ import { CollectionService } from "@bitwarden/admin-console/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { CollectionId } from "@bitwarden/common/types/guid"; import { CollectionId } from "@bitwarden/common/types/guid";
import { AccountService } from "../../auth/abstractions/account.service";
import { getUserId } from "../../auth/services/account.service";
import { getByIds } from "../../platform/misc/rxjs-operators";
import { Cipher } from "../models/domain/cipher"; import { Cipher } from "../models/domain/cipher";
import { CipherView } from "../models/view/cipher.view"; import { CipherView } from "../models/view/cipher.view";
@ -46,9 +49,12 @@ export abstract class CipherAuthorizationService {
* {@link CipherAuthorizationService} * {@link CipherAuthorizationService}
*/ */
export class DefaultCipherAuthorizationService implements CipherAuthorizationService { export class DefaultCipherAuthorizationService implements CipherAuthorizationService {
private activeUserId$ = this.accountService.activeAccount$.pipe(getUserId);
constructor( constructor(
private collectionService: CollectionService, private collectionService: CollectionService,
private organizationService: OrganizationService, private organizationService: OrganizationService,
private accountService: AccountService,
) {} ) {}
/** /**
@ -77,15 +83,14 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer
} }
} }
return this.collectionService return this.collectionService.decryptedCollections$(this.activeUserId$).pipe(
.decryptedCollectionViews$(cipher.collectionIds as CollectionId[]) getByIds(cipher.collectionIds),
.pipe( map((cipherCollections) => {
map((allCollections) => {
const shouldFilter = allowedCollections?.some(Boolean); const shouldFilter = allowedCollections?.some(Boolean);
const collections = shouldFilter const collections = shouldFilter
? allCollections.filter((c) => allowedCollections.includes(c.id as CollectionId)) ? cipherCollections.filter((c) => allowedCollections.includes(c.id as CollectionId))
: allCollections; : cipherCollections;
return collections.some((collection) => collection.manage); return collections.some((collection) => collection.manage);
}), }),
@ -112,9 +117,10 @@ export class DefaultCipherAuthorizationService implements CipherAuthorizationSer
return of(true); return of(true);
} }
return this.collectionService return this.collectionService.decryptedCollections$(this.activeUserId$).pipe(
.decryptedCollectionViews$(cipher.collectionIds as CollectionId[]) getByIds(cipher.collectionIds),
.pipe(map((allCollections) => allCollections.some((collection) => collection.manage))); map((allCollections) => allCollections.some((collection) => collection.manage)),
);
}), }),
shareReplay({ bufferSize: 1, refCount: false }), shareReplay({ bufferSize: 1, refCount: false }),
); );

View File

@ -1,6 +1,6 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component, Input, OnChanges, OnDestroy } from "@angular/core"; import { Component, Input, OnChanges, OnDestroy } from "@angular/core";
import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs"; import { firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; import { CollectionService, CollectionView } from "@bitwarden/admin-console/common";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
@ -9,6 +9,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { isCardExpired } from "@bitwarden/common/autofill/utils"; import { isCardExpired } from "@bitwarden/common/autofill/utils";
import { getByIds } from "@bitwarden/common/platform/misc";
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
@ -104,11 +105,7 @@ export class CipherViewComponent implements OnChanges, OnDestroy {
this.collections = await firstValueFrom( this.collections = await firstValueFrom(
this.collectionService this.collectionService
.decryptedCollections$(this.activeUserId$) .decryptedCollections$(this.activeUserId$)
.pipe( .pipe(getByIds(this.cipher.collectionIds)),
map((allCollections) =>
allCollections.filter((c) => this.cipher.collectionIds.includes(c.id)),
),
),
); );
} }