mirror of
https://github.com/bitwarden/browser
synced 2026-02-24 08:33:29 +00:00
Search builder (#13823)
* Work on SearchBuilderComponent * Get component to not throw errors Co-authored-by: Matt Gibson <mgibson@bitwarden.com> * Rename to filter * Align Buttons Correctly * Filter Build Updates * Add VaultFilterMetadataService * Rename Directory * Emit filter --------- Co-authored-by: Matt Gibson <mgibson@bitwarden.com>
This commit is contained in:
@@ -0,0 +1,118 @@
|
||||
import { firstValueFrom, of } from "rxjs";
|
||||
|
||||
import { CipherType } from "../enums";
|
||||
import { AttachmentView } from "../models/view/attachment.view";
|
||||
import { CipherView } from "../models/view/cipher.view";
|
||||
import { FieldView } from "../models/view/field.view";
|
||||
|
||||
import {
|
||||
VaultFilterMetadata,
|
||||
VaultFilterMetadataService as VaultFilterMetadataService,
|
||||
} from "./vault-filter-metadata.service";
|
||||
|
||||
type TestCipher = {
|
||||
organization?: string;
|
||||
type: CipherType;
|
||||
folderId?: string;
|
||||
fields?: string[];
|
||||
collectionIds?: string[];
|
||||
attachments?: number;
|
||||
};
|
||||
const createCipher = (data: TestCipher) => {
|
||||
const cipher = new CipherView();
|
||||
cipher.organizationId = data.organization ?? null;
|
||||
cipher.type = data.type;
|
||||
cipher.fields = data.fields?.map((f) => {
|
||||
const field = new FieldView();
|
||||
field.name = f;
|
||||
return field;
|
||||
});
|
||||
cipher.collectionIds = data.collectionIds;
|
||||
|
||||
if (data.attachments != null) {
|
||||
const attachments: AttachmentView[] = [];
|
||||
for (let i = 0; i < data.attachments; i++) {
|
||||
attachments.push(new AttachmentView());
|
||||
}
|
||||
cipher.attachments = attachments;
|
||||
}
|
||||
|
||||
return cipher;
|
||||
};
|
||||
|
||||
describe("VaultFilterMetadataService", () => {
|
||||
const sut = new VaultFilterMetadataService();
|
||||
|
||||
describe("collectMetadata", () => {
|
||||
const testData: {
|
||||
name: string;
|
||||
input: CipherView[];
|
||||
output: VaultFilterMetadata;
|
||||
}[] = [
|
||||
{
|
||||
name: "single personal vault cipher",
|
||||
input: [createCipher({ type: CipherType.Card })],
|
||||
output: {
|
||||
vaults: new Set([null]),
|
||||
fieldNames: new Set([]),
|
||||
itemTypes: new Set([CipherType.Card]),
|
||||
folders: new Set([]),
|
||||
collections: new Set([]),
|
||||
anyHaveAttachment: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple different org ciphers",
|
||||
input: [
|
||||
createCipher({
|
||||
organization: "org-one",
|
||||
type: CipherType.Login,
|
||||
attachments: 2,
|
||||
collectionIds: ["one"],
|
||||
fields: ["one", "one"],
|
||||
}),
|
||||
createCipher({
|
||||
organization: "org-one",
|
||||
type: CipherType.Login,
|
||||
attachments: 2,
|
||||
collectionIds: ["one"],
|
||||
fields: ["one", "one"],
|
||||
}),
|
||||
createCipher({
|
||||
organization: "org-two",
|
||||
type: CipherType.Login,
|
||||
attachments: 2,
|
||||
collectionIds: ["one"],
|
||||
fields: ["one", "one"],
|
||||
}),
|
||||
createCipher({
|
||||
organization: "org-two",
|
||||
type: CipherType.Card,
|
||||
attachments: 2,
|
||||
collectionIds: ["three"],
|
||||
fields: ["one", "five"],
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
vaults: new Set(["org-one", "org-two"]),
|
||||
fieldNames: new Set(["one", "five"]),
|
||||
itemTypes: new Set([CipherType.Login, CipherType.Card]),
|
||||
folders: new Set([]),
|
||||
collections: new Set(["one", "three"]),
|
||||
anyHaveAttachment: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
it.each(testData)("$name", async ({ input, output }) => {
|
||||
const actualMetadata = await firstValueFrom(of(input).pipe(sut.collectMetadata()));
|
||||
|
||||
expect(actualMetadata.vaults).toEqual(output.vaults);
|
||||
expect(actualMetadata.fieldNames).toEqual(output.fieldNames);
|
||||
expect(actualMetadata.itemTypes).toEqual(output.itemTypes);
|
||||
expect(actualMetadata.folders).toEqual(output.folders);
|
||||
expect(actualMetadata.collections).toEqual(output.collections);
|
||||
expect(actualMetadata.anyHaveAttachment).toBe(output.anyHaveAttachment);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
import { map } from "rxjs";
|
||||
|
||||
import { CipherType } from "../enums";
|
||||
import { CipherView } from "../models/view/cipher.view";
|
||||
|
||||
export type VaultFilterMetadata = {
|
||||
vaults: Set<string | null>;
|
||||
fieldNames: Set<string>;
|
||||
itemTypes: Set<CipherType>;
|
||||
folders: Set<string>;
|
||||
collections: Set<string>;
|
||||
anyHaveAttachment: boolean;
|
||||
};
|
||||
|
||||
export class VaultFilterMetadataService {
|
||||
collectMetadata() {
|
||||
return map<CipherView[], VaultFilterMetadata>((ciphers) => {
|
||||
return ciphers.reduce<VaultFilterMetadata>(
|
||||
(metadata, cipher) => {
|
||||
// Track type
|
||||
metadata.itemTypes.add(cipher.type);
|
||||
|
||||
// Track vault
|
||||
metadata.vaults.add(cipher.organizationId ?? null);
|
||||
|
||||
// Track all field names
|
||||
if (cipher.fields != null) {
|
||||
for (const field of cipher.fields) {
|
||||
metadata.fieldNames.add(field.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Track all folder ids
|
||||
if (cipher.folderId != null) {
|
||||
metadata.folders.add(cipher.folderId);
|
||||
}
|
||||
|
||||
// Track all collections
|
||||
if (cipher.collectionIds != null) {
|
||||
for (const collectionId of cipher.collectionIds) {
|
||||
metadata.collections.add(collectionId);
|
||||
}
|
||||
}
|
||||
|
||||
// Track if any have an attachment
|
||||
if (cipher.attachments != null && cipher.attachments.length > 0) {
|
||||
metadata.anyHaveAttachment = true;
|
||||
}
|
||||
|
||||
return metadata;
|
||||
},
|
||||
{
|
||||
vaults: new Set<string | null>(),
|
||||
fieldNames: new Set<string>(),
|
||||
itemTypes: new Set<CipherType>(),
|
||||
folders: new Set<string>(),
|
||||
collections: new Set<string>(),
|
||||
anyHaveAttachment: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user