mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 05:13:29 +00:00
[PM-23133] refactor members component (#16703)
* WIP: added new services, refactor members to use billing service and member action service * replace dialog logic and user logic with service implementations * WIP * wip add tests * add tests, continue refactoring * clean up * move BillingConstraintService to billing ownership * fix import * fix seat count not updating if feature flag is disabled * refactor billingMetadata, clean up
This commit is contained in:
@@ -208,7 +208,7 @@ describe("DefaultOrganizationMetadataService", () => {
|
||||
}, 10);
|
||||
});
|
||||
|
||||
it("does not trigger refresh when feature flag is disabled", async () => {
|
||||
it("does trigger refresh when feature flag is disabled", async () => {
|
||||
featureFlagSubject.next(false);
|
||||
|
||||
const mockResponse1 = createMockMetadataResponse(false, 10);
|
||||
@@ -232,11 +232,10 @@ describe("DefaultOrganizationMetadataService", () => {
|
||||
|
||||
service.refreshMetadataCache();
|
||||
|
||||
// wait to ensure no additional invocations
|
||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||
|
||||
expect(invocationCount).toBe(1);
|
||||
expect(billingApiService.getOrganizationBillingMetadata).toHaveBeenCalledTimes(1);
|
||||
expect(invocationCount).toBe(2);
|
||||
expect(billingApiService.getOrganizationBillingMetadata).toHaveBeenCalledTimes(2);
|
||||
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { filter, from, merge, Observable, shareReplay, Subject, switchMap } from "rxjs";
|
||||
import { BehaviorSubject, combineLatest, from, Observable, shareReplay, switchMap } from "rxjs";
|
||||
|
||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||
|
||||
@@ -18,57 +18,56 @@ export class DefaultOrganizationMetadataService implements OrganizationMetadataS
|
||||
private billingApiService: BillingApiServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
private refreshMetadataTrigger = new Subject<void>();
|
||||
private refreshMetadataTrigger = new BehaviorSubject<void>(undefined);
|
||||
|
||||
refreshMetadataCache = () => this.refreshMetadataTrigger.next();
|
||||
refreshMetadataCache = () => {
|
||||
this.metadataCache.clear();
|
||||
this.refreshMetadataTrigger.next();
|
||||
};
|
||||
|
||||
getOrganizationMetadata$ = (
|
||||
organizationId: OrganizationId,
|
||||
): Observable<OrganizationBillingMetadataResponse> =>
|
||||
this.configService
|
||||
.getFeatureFlag$(FeatureFlag.PM25379_UseNewOrganizationMetadataStructure)
|
||||
.pipe(
|
||||
switchMap((featureFlagEnabled) => {
|
||||
return merge(
|
||||
this.getOrganizationMetadataInternal$(organizationId, featureFlagEnabled),
|
||||
this.refreshMetadataTrigger.pipe(
|
||||
filter(() => featureFlagEnabled),
|
||||
switchMap(() =>
|
||||
this.getOrganizationMetadataInternal$(organizationId, featureFlagEnabled, true),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
getOrganizationMetadata$(orgId: OrganizationId): Observable<OrganizationBillingMetadataResponse> {
|
||||
return combineLatest([
|
||||
this.refreshMetadataTrigger,
|
||||
this.configService.getFeatureFlag$(FeatureFlag.PM25379_UseNewOrganizationMetadataStructure),
|
||||
]).pipe(
|
||||
switchMap(([_, featureFlagEnabled]) =>
|
||||
featureFlagEnabled
|
||||
? this.vNextGetOrganizationMetadataInternal$(orgId)
|
||||
: this.getOrganizationMetadataInternal$(orgId),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private getOrganizationMetadataInternal$(
|
||||
organizationId: OrganizationId,
|
||||
featureFlagEnabled: boolean,
|
||||
bypassCache: boolean = false,
|
||||
private vNextGetOrganizationMetadataInternal$(
|
||||
orgId: OrganizationId,
|
||||
): Observable<OrganizationBillingMetadataResponse> {
|
||||
if (!bypassCache && featureFlagEnabled && this.metadataCache.has(organizationId)) {
|
||||
return this.metadataCache.get(organizationId)!;
|
||||
const cacheHit = this.metadataCache.get(orgId);
|
||||
if (cacheHit) {
|
||||
return cacheHit;
|
||||
}
|
||||
|
||||
const metadata$ = from(this.fetchMetadata(organizationId, featureFlagEnabled)).pipe(
|
||||
const result = from(this.fetchMetadata(orgId, true)).pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: false }),
|
||||
);
|
||||
|
||||
if (featureFlagEnabled) {
|
||||
this.metadataCache.set(organizationId, metadata$);
|
||||
}
|
||||
this.metadataCache.set(orgId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
return metadata$;
|
||||
private getOrganizationMetadataInternal$(
|
||||
organizationId: OrganizationId,
|
||||
): Observable<OrganizationBillingMetadataResponse> {
|
||||
return from(this.fetchMetadata(organizationId, false)).pipe(
|
||||
shareReplay({ bufferSize: 1, refCount: false }),
|
||||
);
|
||||
}
|
||||
|
||||
private async fetchMetadata(
|
||||
organizationId: OrganizationId,
|
||||
featureFlagEnabled: boolean,
|
||||
): Promise<OrganizationBillingMetadataResponse> {
|
||||
if (featureFlagEnabled) {
|
||||
return await this.billingApiService.getOrganizationBillingMetadataVNext(organizationId);
|
||||
}
|
||||
|
||||
return await this.billingApiService.getOrganizationBillingMetadata(organizationId);
|
||||
return featureFlagEnabled
|
||||
? await this.billingApiService.getOrganizationBillingMetadataVNext(organizationId)
|
||||
: await this.billingApiService.getOrganizationBillingMetadata(organizationId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user