mirror of
https://github.com/bitwarden/browser
synced 2026-02-05 19:23:19 +00:00
Move getAllFromApiForOrganization to use SDK
This commit is contained in:
@@ -1490,6 +1490,116 @@ describe("Cipher Service", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllFromApiForOrganization()", () => {
|
||||
let mockSdkClient: any;
|
||||
let mockCiphersSdk: any;
|
||||
let mockAdminSdk: any;
|
||||
let mockVaultSdk: any;
|
||||
const testOrgId = "4ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b21" as OrganizationId;
|
||||
const mockSdkCipherView1 = {
|
||||
id: "5ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b22",
|
||||
name: "Test Cipher 1",
|
||||
};
|
||||
const mockSdkCipherView2 = {
|
||||
id: "6ff8c0b2-1d3e-4f8c-9b2d-1d3e4f8c0b23",
|
||||
name: "Test Cipher 2",
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock the SDK client chain for list_org_ciphers
|
||||
mockAdminSdk = {
|
||||
list_org_ciphers: jest.fn().mockResolvedValue({
|
||||
successes: [mockSdkCipherView1, mockSdkCipherView2],
|
||||
failures: [],
|
||||
}),
|
||||
};
|
||||
mockCiphersSdk = {
|
||||
admin: jest.fn().mockReturnValue(mockAdminSdk),
|
||||
};
|
||||
mockVaultSdk = {
|
||||
ciphers: jest.fn().mockReturnValue(mockCiphersSdk),
|
||||
};
|
||||
const mockSdkValue = {
|
||||
vault: jest.fn().mockReturnValue(mockVaultSdk),
|
||||
};
|
||||
mockSdkClient = {
|
||||
take: jest.fn().mockReturnValue({
|
||||
value: mockSdkValue,
|
||||
[Symbol.dispose]: jest.fn(),
|
||||
}),
|
||||
};
|
||||
|
||||
// Mock sdkService to return the mock client
|
||||
sdkService.userClient$.mockReturnValue(of(mockSdkClient));
|
||||
});
|
||||
|
||||
it("should call apiService.getCiphersOrganization when feature flag is disabled", async () => {
|
||||
configService.getFeatureFlag
|
||||
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
|
||||
.mockResolvedValue(false);
|
||||
|
||||
const mockResponse = {
|
||||
data: [],
|
||||
} as any;
|
||||
|
||||
const apiSpy = jest
|
||||
.spyOn(apiService, "getCiphersOrganization")
|
||||
.mockResolvedValue(mockResponse);
|
||||
|
||||
await cipherService.getAllFromApiForOrganization(testOrgId, true);
|
||||
|
||||
expect(apiSpy).toHaveBeenCalledWith(testOrgId, true);
|
||||
expect(mockSdkClient.take).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should call apiService.getCiphersOrganization without includeMemberItems when not provided", async () => {
|
||||
configService.getFeatureFlag
|
||||
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
|
||||
.mockResolvedValue(false);
|
||||
|
||||
const mockResponse = { data: [] } as any;
|
||||
const apiSpy = jest
|
||||
.spyOn(apiService, "getCiphersOrganization")
|
||||
.mockResolvedValue(mockResponse);
|
||||
|
||||
await cipherService.getAllFromApiForOrganization(testOrgId);
|
||||
|
||||
expect(apiSpy).toHaveBeenCalledWith(testOrgId, undefined);
|
||||
expect(mockSdkClient.take).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should use SDK to list organization ciphers when feature flag is enabled", async () => {
|
||||
configService.getFeatureFlag
|
||||
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
|
||||
.mockResolvedValue(true);
|
||||
|
||||
const apiSpy = jest.spyOn(apiService, "getCiphersOrganization");
|
||||
|
||||
const result = await cipherService.getAllFromApiForOrganization(testOrgId, true);
|
||||
|
||||
expect(mockSdkClient.take).toHaveBeenCalled();
|
||||
expect(mockAdminSdk.list_org_ciphers).toHaveBeenCalledWith(testOrgId, true);
|
||||
expect(apiSpy).not.toHaveBeenCalled();
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toBeInstanceOf(CipherView);
|
||||
expect(result[1]).toBeInstanceOf(CipherView);
|
||||
});
|
||||
|
||||
it("should use SDK with includeMemberItems=false when not provided", async () => {
|
||||
configService.getFeatureFlag
|
||||
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
|
||||
.mockResolvedValue(true);
|
||||
|
||||
const apiSpy = jest.spyOn(apiService, "getCiphersOrganization");
|
||||
|
||||
await cipherService.getAllFromApiForOrganization(testOrgId);
|
||||
|
||||
expect(mockSdkClient.take).toHaveBeenCalled();
|
||||
expect(mockAdminSdk.list_org_ciphers).toHaveBeenCalledWith(testOrgId, false);
|
||||
expect(apiSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("replace (no upsert)", () => {
|
||||
// In order to set up initial state we need to manually update the encrypted state
|
||||
// which will result in an emission. All tests will have this baseline emission.
|
||||
|
||||
@@ -723,6 +723,13 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
organizationId: string,
|
||||
includeMemberItems?: boolean,
|
||||
): Promise<CipherView[]> {
|
||||
const useSdk = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.PM27632_SdkCipherCrudOperations,
|
||||
);
|
||||
if (useSdk) {
|
||||
return this.getAllFromApiForOrganization_sdk(organizationId, includeMemberItems ?? false);
|
||||
}
|
||||
|
||||
const response = await this.apiService.getCiphersOrganization(
|
||||
organizationId,
|
||||
includeMemberItems,
|
||||
@@ -730,6 +737,48 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
return await this.decryptOrganizationCiphersResponse(response, organizationId);
|
||||
}
|
||||
|
||||
private async getAllFromApiForOrganization_sdk(
|
||||
organizationId: string,
|
||||
includeMemberItems: boolean,
|
||||
): Promise<CipherView[]> {
|
||||
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(map((a) => a?.id)));
|
||||
if (!userId) {
|
||||
throw new Error("User ID is required");
|
||||
}
|
||||
|
||||
const result = await firstValueFrom(
|
||||
this.sdkService.userClient$(userId).pipe(
|
||||
switchMap(async (sdk) => {
|
||||
if (!sdk) {
|
||||
throw new Error("SDK not available");
|
||||
}
|
||||
using ref = sdk.take();
|
||||
const decryptResult = await ref.value
|
||||
.vault()
|
||||
.ciphers()
|
||||
.admin()
|
||||
.list_org_ciphers(asUuid(organizationId), includeMemberItems);
|
||||
|
||||
// Convert successful decryptions to CipherView[]
|
||||
const cipherViews = decryptResult.successes.map((sdkCipherView: any) =>
|
||||
CipherView.fromSdkCipherView(sdkCipherView),
|
||||
);
|
||||
|
||||
// Sort by locale (matching existing behavior)
|
||||
cipherViews.sort(this.getLocaleSortingFunction());
|
||||
|
||||
return cipherViews;
|
||||
}),
|
||||
catchError((error: unknown) => {
|
||||
this.logService.error(`Failed to list organization ciphers: ${error}`);
|
||||
return [];
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async getManyFromApiForOrganization(organizationId: string): Promise<CipherView[]> {
|
||||
const r = await this.apiService.send(
|
||||
"GET",
|
||||
|
||||
Reference in New Issue
Block a user