import { createSelector } from 'reselect'; import { fuzzyMatchAll } from '../../common/misc'; import type { BaseModel } from '../../models'; import { GrpcRequest, isGrpcRequest } from '../../models/grpc-request'; import { isRequest, Request } from '../../models/request'; import { isRequestGroup, RequestGroup } from '../../models/request-group'; import { selectActiveWorkspace, selectActiveWorkspaceMeta, selectCollapsedRequestGroups, selectEntitiesChildrenMap, selectPinnedRequests, } from './selectors'; type SidebarModel = Request | GrpcRequest | RequestGroup; export const shouldShowInSidebar = (model: BaseModel): boolean => isRequest(model) || isGrpcRequest(model) || isRequestGroup(model); export const shouldIgnoreChildrenOf = (model: SidebarModel): boolean => isRequest(model) || isGrpcRequest(model); export const sortByMetaKeyOrId = (a: SidebarModel, b: SidebarModel): number => { if (a.metaSortKey === b.metaSortKey) { return a._id > b._id ? -1 : 1; // ascending } else { return a.metaSortKey < b.metaSortKey ? -1 : 1; // descending } }; interface Child { doc: SidebarModel; hidden: boolean; collapsed: boolean; pinned: boolean; children: Child[]; } export interface SidebarChildren { all: Child[]; pinned: Child[]; } export const selectSidebarChildren = createSelector( selectCollapsedRequestGroups, selectPinnedRequests, selectActiveWorkspace, selectActiveWorkspaceMeta, selectEntitiesChildrenMap, (collapsed, pinned, activeWorkspace, activeWorkspaceMeta, childrenMap): SidebarChildren => { if (!activeWorkspace) { return { all: [], pinned: [] }; } const sidebarFilter = activeWorkspaceMeta ? activeWorkspaceMeta.sidebarFilter : ''; function next(parentId: string, pinnedChildren: Child[]) { const children: SidebarModel[] = (childrenMap[parentId] || []) .filter(shouldShowInSidebar) .sort(sortByMetaKeyOrId); if (children.length > 0) { return children.map(c => { const child: Child = { doc: c, hidden: false, collapsed: !!collapsed[c._id], pinned: !!pinned[c._id], children: [], }; if (child.pinned) { pinnedChildren.push(child); } // Don't add children of requests child.children = shouldIgnoreChildrenOf(c) ? [] : next(c._id, pinnedChildren); return child; }); } else { return []; } } function matchChildren(children: Child[], parentNames: string[] = []) { // Bail early if no filter defined if (!sidebarFilter) { return children; } for (const child of children) { // Gather all parents so we can match them too matchChildren(child.children, [...parentNames, child.doc.name]); const hasMatchedChildren = child.children.find(c => c.hidden === false); // Try to match request attributes const name = child.doc.name; const method = isGrpcRequest(child.doc) ? 'gRPC' : isRequest(child.doc) ? child.doc.method : ''; const match = fuzzyMatchAll(sidebarFilter, [name, method, ...parentNames], { splitSpace: true, }); // Update hidden state depending on whether it matched const matched = hasMatchedChildren || match; child.hidden = !matched; } return children; } const pinnedChildren: Child[] = []; const childrenTree = next(activeWorkspace._id, pinnedChildren); const matchedChildren = matchChildren(childrenTree); return { pinned: pinnedChildren, all: matchedChildren, }; }, );