mirror of
https://github.com/bitwarden/browser
synced 2025-12-23 11:43:46 +00:00
[EC-14] Refactor vault filter (#3440)
* [EC-14] initial refactoring of vault filter * [EC-14] return observable trees for all filters with head node * [EC-14] Remove bindings on callbacks * [EC-14] fix formatting on disabled orgs * [EC-14] hide MyVault if personal org policy * [EC-14] add check for single org policy * [EC-14] add policies to org and change node constructor * [EC-14] don't show options if personal vault policy * [EC-14] default to all vaults * [EC-14] add default selection to filters * [EC-14] finish filter model callbacks * [EC-14] finish filter functionality and begin cleaning up * [EC-14] clean up old components and start on org vault * [EC-14] loop through filters for presentation * [EC-14] refactor VaultFilterService and put filter presentation data back into Vault Filter component. Remove VaultService * [EC-14] begin refactoring org vault * [EC-14] Refactor Vault Filter Service to use observables * [EC-14] finish org vault filter * [EC-14] fix vault model tests * [EC-14] fix org service calls * [EC-14] pull refactor out of shared code * [EC-14] include head node for collections even if collections aren't loaded yet * [EC-14] fix url params for vaults * [EC-14] remove comments * [EC-14] Remove unnecesary getter for org on vault filter * [EC-14] fix linter * [EC-14] fix prettier * [EC-14] add deprecated methods to collection service for desktop and browser * [EC-14] simplify cipher type node check * [EC-14] add getters to vault filter model * [EC-14] refactor how we build the filter list into methods * [EC-14] add getters to build filter method * [EC-14] remove param ids if false * [EC-14] fix collapsing nodes * [EC-14] add specific type to search placeholder * [EC-14] remove extra constructor and comment from org vault filter * [EC-14] extract subscription callback to methods * [EC-14] Remove unecessary await * [EC-14] Remove ternary operators while building org filter * [EC-14] remove unnecessary deps array in vault filter service declaration * [EC-14] consolidate new models into one file * [EC-14] initialize nested observable inside of service Signed-off-by: Jacob Fink <jfink@bitwarden.com> * [EC-14] change how we load orgs into the vault filter and select the default filter * [EC-14] remove get from getters name * [EC-14] remove eslint-disable comment * [EC-14] move vault filter service abstraction to angular folder and separate * [EC-14] rename filter types and delete VaultFilterLabel * [EC-14] remove changes to workspace file * [EC-14] remove deprecated service from jslib module * [EC-14] remove any remaining files from common code * [EC-14] consolidate vault filter components into components folder * [EC-14] simplify method call * [EC-14] refactor the vault filter service - orgs now have observable property - BehaviorSubjects have been migrated to ReplaySubjects if they don't need starting value - added unit tests - fix small error when selecting org badge of personal vault - renamed some properties * [EC-14] replace mergeMap with switchMap in vault filter service * [EC-14] early return to prevent nesting * [EC-14] clean up filterCollections method * [EC-14] use isDeleted helper in html * [EC-14] add jsdoc comments to ServiceUtils * [EC-14] fix linter * [EC-14] use array.slice instead of setting length * Update apps/web/src/app/vault/vault-filter/services/vault-filter.service.ts Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * [EC-14] add missing high level jsdoc description * [EC-14] fix storybook absolute imports * [EC-14] delete vault-shared.module * [EC-14] change search placeholder text to getter and add missing strings * [EC-14] remove two way binding from search text in vault filter * [EC-14] removed all binding from search text and just use input event * [EC-14] remove async from apply vault filter * [EC-14] remove circular observable calls in vault filter service Co-authored-by: Thomas Rittson <eliykat@users.noreply.github.com> * [EC-14] move collapsed nodes to vault filter section * [EC-14] deconstruct filter section inside component * [EC-14] fix merge conflicts and introduce refactored organization service to vault filter service * [EC-14] remove mutation from filter builders * [EC-14] fix styling on buildFolderTree * [EC-14] remove leftover folder-filters reference and use ternary for collapse icon * [EC-14] remove unecessary checks * [EC-14] stop rebuilding filters when the organization changes * [EC-14] Move subscription out of setter in vault filter section * [EC-14] remove extra policy service methods from vault filter service * [EC-14] remove new methods from old vault-filter.service * [EC-14] Use vault filter service in vault components * [EC-14] reload collections from vault now that we have vault filter service * [EC-14] remove currentFilterCollections in vault filter component * [EC-14] change VaultFilterType to more specific OrganizationFilter in organization-options * [EC-14] include org check in isNodeSelected * [EC-14] add getters to filter function, fix storybook, and add test for All Collections * [EC-14] show org options even if there's a personal vault policy * [EC-14] use !"AllCollections" instead of just !null * [EC-14] Remove extra org Subject in vault filter service * [EC-14] remove null check from vault search text * [EC-14] replace store/build names with set/get. Remove extra call to setOrganizationFilter * [EC-14] add take(1) to subscribe in test * [EC-14] move init logic in org vault filter component to ngOnInit * [EC-14] Fix linter * [EC-14] revert change to vault filter model * [EC-14] be specific about ignoring All Collections * [EC-14] move observable init logic to beforeEach in test * [EC-14] make buildAllFilters return something to reduce side effects Signed-off-by: Jacob Fink <jfink@bitwarden.com> Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Co-authored-by: Thomas Rittson <eliykat@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { CollectionView } from "@bitwarden/common/models/view/collectionView";
|
||||
import { FolderView } from "@bitwarden/common/models/view/folderView";
|
||||
|
||||
import { DynamicTreeNode } from "../vault/vault-filter/models/dynamic-tree-node.model";
|
||||
|
||||
/**
|
||||
* @deprecated August 30 2022: Use new VaultFilterService with observables
|
||||
*/
|
||||
export abstract class DeprecatedVaultFilterService {
|
||||
buildOrganizations: () => Promise<Organization[]>;
|
||||
buildNestedFolders: (organizationId?: string) => Observable<DynamicTreeNode<FolderView>>;
|
||||
buildCollections: (organizationId?: string) => Promise<DynamicTreeNode<CollectionView>>;
|
||||
buildCollapsedFilterNodes: () => Promise<Set<string>>;
|
||||
storeCollapsedFilterNodes: (collapsedFilterNodes: Set<string>) => Promise<void>;
|
||||
checkForSingleOrganizationPolicy: () => Promise<boolean>;
|
||||
checkForPersonalOwnershipPolicy: () => Promise<boolean>;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { firstValueFrom, Observable } from "rxjs";
|
||||
|
||||
import { DeprecatedVaultFilterService } from "@bitwarden/angular/abstractions/deprecated-vault-filter.service";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { ITreeNodeObject } from "@bitwarden/common/models/domain/treeNode";
|
||||
import { CollectionView } from "@bitwarden/common/models/view/collectionView";
|
||||
@@ -8,8 +9,9 @@ import { FolderView } from "@bitwarden/common/models/view/folderView";
|
||||
|
||||
import { DynamicTreeNode } from "../models/dynamic-tree-node.model";
|
||||
import { VaultFilter } from "../models/vault-filter.model";
|
||||
import { VaultFilterService } from "../services/vault-filter.service";
|
||||
|
||||
// TODO: Replace with refactored web vault filter component
|
||||
// and refactor desktop/browser vault filters
|
||||
@Directive()
|
||||
export class VaultFilterComponent implements OnInit {
|
||||
@Input() activeFilter: VaultFilter = new VaultFilter();
|
||||
@@ -31,7 +33,7 @@ export class VaultFilterComponent implements OnInit {
|
||||
collections: DynamicTreeNode<CollectionView>;
|
||||
folders$: Observable<DynamicTreeNode<FolderView>>;
|
||||
|
||||
constructor(protected vaultFilterService: VaultFilterService) {}
|
||||
constructor(protected vaultFilterService: DeprecatedVaultFilterService) {}
|
||||
|
||||
get displayCollections() {
|
||||
return this.collections?.fullList != null && this.collections.fullList.length > 0;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { TreeNode } from "@bitwarden/common/models/domain/treeNode";
|
||||
import { CollectionView } from "@bitwarden/common/models/view/collectionView";
|
||||
import { FolderView } from "@bitwarden/common/models/view/folderView";
|
||||
import { ITreeNodeObject, TreeNode } from "@bitwarden/common/models/domain/treeNode";
|
||||
|
||||
export class DynamicTreeNode<T extends CollectionView | FolderView> {
|
||||
export class DynamicTreeNode<T extends ITreeNodeObject> {
|
||||
fullList: T[];
|
||||
nestedList: TreeNode<T>[];
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { firstValueFrom, from, mergeMap, Observable } from "rxjs";
|
||||
import { firstValueFrom, mergeMap, Observable, from } from "rxjs";
|
||||
|
||||
import { DeprecatedVaultFilterService as DeprecatedVaultFilterServiceAbstraction } from "@bitwarden/angular/abstractions/deprecated-vault-filter.service";
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/abstractions/collection.service";
|
||||
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
||||
@@ -19,7 +20,7 @@ import { DynamicTreeNode } from "../models/dynamic-tree-node.model";
|
||||
const NestingDelimiter = "/";
|
||||
|
||||
@Injectable()
|
||||
export class VaultFilterService {
|
||||
export class VaultFilterService implements DeprecatedVaultFilterServiceAbstraction {
|
||||
constructor(
|
||||
protected stateService: StateService,
|
||||
protected organizationService: OrganizationService,
|
||||
@@ -106,6 +107,6 @@ export class VaultFilterService {
|
||||
const folders = await this.getAllFoldersNested(
|
||||
await firstValueFrom(this.folderService.folderViews$)
|
||||
);
|
||||
return ServiceUtils.getTreeNodeObject(folders, id) as TreeNode<FolderView>;
|
||||
return ServiceUtils.getTreeNodeObjectFromList(folders, id) as TreeNode<FolderView>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { ITreeNodeObject, TreeNode } from "../models/domain/treeNode";
|
||||
|
||||
export class ServiceUtils {
|
||||
/**
|
||||
* Recursively adds a node to nodeTree
|
||||
* @param {TreeNode<ITreeNodeObject>[]} nodeTree - An array of TreeNodes that the node will be added to
|
||||
* @param {number} partIndex - Index of the `parts` array that is being processed
|
||||
* @param {string[]} parts - Array of strings that represent the path to the `obj` node
|
||||
* @param {ITreeNodeObject} obj - The node to be added to the tree
|
||||
* @param {ITreeNodeObject} parent - The parent node of the `obj` node
|
||||
* @param {string} delimiter - The delimiter used to split the path string
|
||||
*/
|
||||
static nestedTraverse(
|
||||
nodeTree: TreeNode<ITreeNodeObject>[],
|
||||
partIndex: number,
|
||||
@@ -22,7 +31,7 @@ export class ServiceUtils {
|
||||
}
|
||||
if (end && nodeTree[i].node.id !== obj.id) {
|
||||
// Another node with the same name.
|
||||
nodeTree.push(new TreeNode(obj, partName, parent));
|
||||
nodeTree.push(new TreeNode(obj, parent, partName));
|
||||
return;
|
||||
}
|
||||
ServiceUtils.nestedTraverse(
|
||||
@@ -38,7 +47,7 @@ export class ServiceUtils {
|
||||
|
||||
if (nodeTree.filter((n) => n.node.name === partName).length === 0) {
|
||||
if (end) {
|
||||
nodeTree.push(new TreeNode(obj, partName, parent));
|
||||
nodeTree.push(new TreeNode(obj, parent, partName));
|
||||
return;
|
||||
}
|
||||
const newPartName = parts[partIndex] + delimiter + parts[partIndex + 1];
|
||||
@@ -53,7 +62,37 @@ export class ServiceUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a tree for a node with a matching `id`
|
||||
* @param {TreeNode<ITreeNodeObject>} nodeTree - A single TreeNode branch that will be searched
|
||||
* @param {string} id - The id of the node to be found
|
||||
* @returns {TreeNode<ITreeNodeObject>} The node with a matching `id`
|
||||
*/
|
||||
static getTreeNodeObject(
|
||||
nodeTree: TreeNode<ITreeNodeObject>,
|
||||
id: string
|
||||
): TreeNode<ITreeNodeObject> {
|
||||
if (nodeTree.node.id === id) {
|
||||
return nodeTree;
|
||||
}
|
||||
for (let i = 0; i < nodeTree.children.length; i++) {
|
||||
if (nodeTree.children[i].children != null) {
|
||||
const node = ServiceUtils.getTreeNodeObject(nodeTree.children[i], id);
|
||||
if (node !== null) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches an array of tree nodes for a node with a matching `id`
|
||||
* @param {TreeNode<ITreeNodeObject>} nodeTree - An array of TreeNode branches that will be searched
|
||||
* @param {string} id - The id of the node to be found
|
||||
* @returns {TreeNode<ITreeNodeObject>} The node with a matching `id`
|
||||
*/
|
||||
static getTreeNodeObjectFromList(
|
||||
nodeTree: TreeNode<ITreeNodeObject>[],
|
||||
id: string
|
||||
): TreeNode<ITreeNodeObject> {
|
||||
@@ -61,7 +100,7 @@ export class ServiceUtils {
|
||||
if (nodeTree[i].node.id === id) {
|
||||
return nodeTree[i];
|
||||
} else if (nodeTree[i].children != null) {
|
||||
const node = ServiceUtils.getTreeNodeObject(nodeTree[i].children, id);
|
||||
const node = ServiceUtils.getTreeNodeObjectFromList(nodeTree[i].children, id);
|
||||
if (node !== null) {
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -137,6 +137,10 @@ export class Organization {
|
||||
);
|
||||
}
|
||||
|
||||
get canUseAdminCollections() {
|
||||
return this.canEditAnyCollection;
|
||||
}
|
||||
|
||||
get canDeleteAnyCollection() {
|
||||
return (
|
||||
this.isAdmin ||
|
||||
|
||||
@@ -3,10 +3,15 @@ export class TreeNode<T extends ITreeNodeObject> {
|
||||
node: T;
|
||||
children: TreeNode<T>[] = [];
|
||||
|
||||
constructor(node: T, name: string, parent: T) {
|
||||
constructor(node: T, parent: T, name?: string, id?: string) {
|
||||
this.parent = parent;
|
||||
this.node = node;
|
||||
this.node.name = name;
|
||||
if (name) {
|
||||
this.node.name = name;
|
||||
}
|
||||
if (id) {
|
||||
this.node.id = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +91,10 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
return decryptedCollections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated August 30 2022: Moved to new Vault Filter Service
|
||||
* Remove when Desktop and Browser are updated
|
||||
*/
|
||||
async getAllNested(collections: CollectionView[] = null): Promise<TreeNode<CollectionView>[]> {
|
||||
if (collections == null) {
|
||||
collections = await this.getAllDecrypted();
|
||||
@@ -106,9 +110,13 @@ export class CollectionService implements CollectionServiceAbstraction {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated August 30 2022: Moved to new Vault Filter Service
|
||||
* Remove when Desktop and Browser are updated
|
||||
*/
|
||||
async getNested(id: string): Promise<TreeNode<CollectionView>> {
|
||||
const collections = await this.getAllNested();
|
||||
return ServiceUtils.getTreeNodeObject(collections, id) as TreeNode<CollectionView>;
|
||||
return ServiceUtils.getTreeNodeObjectFromList(collections, id) as TreeNode<CollectionView>;
|
||||
}
|
||||
|
||||
async upsert(collection: CollectionData | CollectionData[]): Promise<any> {
|
||||
|
||||
Reference in New Issue
Block a user