From 023cdbd61633053e19d508b760f594b0e1f58556 Mon Sep 17 00:00:00 2001 From: Lisa Knox Date: Fri, 10 May 2024 14:40:42 -0400 Subject: [PATCH] 21364: added backup search when first search after f5 fails Arvados-DCO-1.1-Signed-off-by: Lisa Knox --- .../data-explorer/data-explorer.tsx | 1 - .../store/search-bar/search-bar-actions.ts | 7 +++ .../store/search-bar/search-bar-reducer.ts | 5 ++ .../search-results-middleware-service.ts | 54 +++++++++++++++++-- .../search-bar/search-bar-view.tsx | 22 ++++++++ .../search-bar/search-bar.tsx | 10 ++-- 6 files changed, 90 insertions(+), 9 deletions(-) diff --git a/services/workbench2/src/components/data-explorer/data-explorer.tsx b/services/workbench2/src/components/data-explorer/data-explorer.tsx index e29ff9c55e..1a77dccf61 100644 --- a/services/workbench2/src/components/data-explorer/data-explorer.tsx +++ b/services/workbench2/src/components/data-explorer/data-explorer.tsx @@ -169,7 +169,6 @@ export const DataExplorer = withStyles(styles)( setCheckedListOnStore, checkedList, working, - page, } = this.props; return ( (), MOVE_DOWN: ofType<{}>(), SELECT_FIRST_ITEM: ofType<{}>(), + SET_SEARCH_OFFSETS: ofType>(), }); export type SearchBarActions = UnionOf; @@ -430,3 +431,9 @@ export const moveDown = () => (dispatch: Dispatch) => { dispatch(searchBarActions.MOVE_DOWN()); }; + +export const setSearchOffsets = (sessionId: string, offset: number) => { + return (dispatch: Dispatch) => { + dispatch(searchBarActions.SET_SEARCH_OFFSETS({id:[sessionId], offset })); + }; +} \ No newline at end of file diff --git a/services/workbench2/src/store/search-bar/search-bar-reducer.ts b/services/workbench2/src/store/search-bar/search-bar-reducer.ts index 05b75bf99c..84c17438a7 100644 --- a/services/workbench2/src/store/search-bar/search-bar-reducer.ts +++ b/services/workbench2/src/store/search-bar/search-bar-reducer.ts @@ -24,6 +24,7 @@ interface SearchBar { savedQueries: SearchBarAdvancedFormData[]; recentQueries: string[]; selectedItem: SearchBarSelectedItem; + searchOffsets: Record; } export enum SearchView { @@ -43,6 +44,7 @@ const initialState: SearchBar = { id: '', query: '' }, + searchOffsets: {}, }; const makeSelectedItem = (id: string, query?: string): SearchBarSelectedItem => ({ id, query: query ? query : id }); @@ -143,5 +145,8 @@ export const searchBarReducer = (state = initialState, action: SearchBarActions) selectedItem }; }, + SET_SEARCH_OFFSETS: ({id, offset}) => { + return {...state, searchOffsets: {...state.searchOffsets, [id]: offset}}; + }, default: () => state }); diff --git a/services/workbench2/src/store/search-results-panel/search-results-middleware-service.ts b/services/workbench2/src/store/search-results-panel/search-results-middleware-service.ts index 11462c0618..2c444813a1 100644 --- a/services/workbench2/src/store/search-results-panel/search-results-middleware-service.ts +++ b/services/workbench2/src/store/search-results-panel/search-results-middleware-service.ts @@ -18,6 +18,7 @@ import { getSearchSessions, queryToFilters, getAdvancedDataFromQuery, + setSearchOffsets, } from 'store/search-bar/search-bar-actions'; import { getSortColumn } from "store/data-explorer/data-explorer-reducer"; import { FilterBuilder, joinFilters } from 'services/api/filter-builder'; @@ -28,6 +29,8 @@ import { ResourceKind } from 'models/resource'; import { ContainerRequestResource } from 'models/container-request'; import { progressIndicatorActions } from 'store/progress-indicator/progress-indicator-actions'; import { dataExplorerActions } from 'store/data-explorer/data-explorer-action'; +import { Session } from 'models/session'; +import { SEARCH_RESULTS_PANEL_ID } from 'store/search-results-panel/search-results-panel-actions'; export class SearchResultsMiddlewareService extends DataExplorerMiddlewareService { constructor(private services: ServiceRepository, id: string) { @@ -50,7 +53,7 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic items: [] as GroupContentsResource[], kind: '', offset: 0, - limit: 10 + limit: 50 }; if (criteriaChanged) { @@ -69,6 +72,9 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic sessions.forEach(session => { const params = getParams(dataExplorer, searchValue, session.apiRevision); + //this prevents double fetching of the same search results when a new session is logged in + api.dispatch(setSearchOffsets(session.clusterId, params.offset )); + this.services.groupsService.contents('', params, session) .then((response) => { api.dispatch(updateResources(response.items)); @@ -100,6 +106,48 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic } } +export const searchSingleCluster = (session: Session, searchValue: string) => + (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const state = getState(); + const dataExplorer = getDataExplorer(state.dataExplorer, SEARCH_RESULTS_PANEL_ID); + + if (searchValue.trim() === '') { + return; + } + + const params = getParams(dataExplorer, searchValue, session.apiRevision); + + // If the clusterId & search offset has already been fetched, we don't need to fetch the results again + if(state.searchBar.searchOffsets[session.clusterId] === params.offset) { + return; + } + + dispatch(progressIndicatorActions.START_WORKING(SEARCH_RESULTS_PANEL_ID)) + + services.groupsService.contents('', params, session) + .then((response) => { + dispatch(setSearchOffsets(session.clusterId, params.offset )); + dispatch(updateResources(response.items)); + dispatch(appendItems(response)); + // Request all containers for process status to be available + const containerRequests = response.items.filter((item) => item.kind === ResourceKind.CONTAINER_REQUEST) as ContainerRequestResource[]; + const containerUuids = containerRequests.map(container => container.containerUuid).filter(uuid => uuid !== null) as string[]; + containerUuids.length && services.containerService + .list({ + filters: new FilterBuilder() + .addIn('uuid', containerUuids) + .getFilters() + }, false) + .then((containers) => { + dispatch(updateResources(containers.items)); + }); + }).catch(() => { + dispatch(couldNotFetchSearchResults(session.clusterId)); + dispatch(progressIndicatorActions.STOP_WORKING(SEARCH_RESULTS_PANEL_ID)) + }); + dispatch(progressIndicatorActions.STOP_WORKING(SEARCH_RESULTS_PANEL_ID)) +} + const typeFilters = (columns: DataColumns) => serializeResourceTypeFilters(getDataExplorerColumnFilters(columns, ProjectPanelColumnNames.TYPE)); export const getParams = (dataExplorer: DataExplorer, query: string, apiRevision: number) => ({ @@ -142,10 +190,6 @@ export const setItems = (listResults: ListResults) => const resetItemsAvailable = () => searchResultsPanelActions.RESET_ITEMS_AVAILABLE(); -const setItemsAvailable = (id: string, itemsAvailable: number) => { - -} - export const appendItems = (listResults: ListResults) => searchResultsPanelActions.APPEND_ITEMS({ ...listResultsToDataExplorerItemsMeta(listResults), diff --git a/services/workbench2/src/views-components/search-bar/search-bar-view.tsx b/services/workbench2/src/views-components/search-bar/search-bar-view.tsx index eba281c9f0..0c08e38b7d 100644 --- a/services/workbench2/src/views-components/search-bar/search-bar-view.tsx +++ b/services/workbench2/src/views-components/search-bar/search-bar-view.tsx @@ -24,6 +24,7 @@ import { KEY_CODE_DOWN, KEY_CODE_ESC, KEY_CODE_UP, KEY_ENTER } from "common/code import { debounce } from "debounce"; import { Vocabulary } from "models/vocabulary"; import { connectVocabulary } from "../resource-properties-form/property-field-common"; +import { Session } from "models/session"; type CssRules = "container" | "containerSearchViewOpened" | "input" | "view"; @@ -64,6 +65,7 @@ interface SearchBarViewDataProps { isPopoverOpen: boolean; debounce?: number; vocabulary?: Vocabulary; + sessions: Session[]; } export type SearchBarActionProps = SearchBarViewActionProps & @@ -81,6 +83,7 @@ interface SearchBarViewActionProps { moveUp: () => void; moveDown: () => void; setAdvancedDataFromSearchValue: (search: string, vocabulary?: Vocabulary) => void; + searchSingleCluster: (session: Session, searchValue: string) => any; } type SearchBarViewProps = SearchBarDataProps & SearchBarActionProps & WithStyles; @@ -137,6 +140,10 @@ export const SearchBarView = compose( withStyles(styles) )( class extends React.Component { + state={ + loggedInSessions: [], + } + debouncedSearch = debounce(() => { this.props.onSearch(this.props.searchValue); }, 1000); @@ -151,6 +158,21 @@ export const SearchBarView = compose( this.props.onSubmit(event); }; + componentDidMount(): void { + this.setState({ loggedInSessions: this.props.sessions.filter((ss) => ss.loggedIn && ss.userIsActive)}); + } + + componentDidUpdate( prevProps: Readonly, prevState: Readonly<{loggedInSessions: Session[]}>, snapshot?: any ): void { + if (prevProps.sessions !== this.props.sessions) { + this.setState({ loggedInSessions: this.props.sessions.filter((ss) => ss.loggedIn)}); + } + //if a new session is logged in after a search is started, search the new cluster and append those to the results + if(prevState.loggedInSessions.length !== this.state.loggedInSessions.length){ + const newLogin = this.state.loggedInSessions.filter((ss) => !prevState.loggedInSessions.includes(ss)); + this.props.searchSingleCluster(newLogin[0], this.props.searchValue); + } + } + componentWillUnmount() { this.debouncedSearch.clear(); } diff --git a/services/workbench2/src/views-components/search-bar/search-bar.tsx b/services/workbench2/src/views-components/search-bar/search-bar.tsx index 6a4d2a620e..92175b6ec4 100644 --- a/services/workbench2/src/views-components/search-bar/search-bar.tsx +++ b/services/workbench2/src/views-components/search-bar/search-bar.tsx @@ -21,8 +21,10 @@ import { import { SearchBarView, SearchBarActionProps, SearchBarDataProps } from 'views-components/search-bar/search-bar-view'; import { SearchBarAdvancedFormData } from 'models/search-bar'; import { Vocabulary } from 'models/vocabulary'; +import { searchSingleCluster } from 'store/search-results-panel/search-results-middleware-service'; +import { Session } from 'models/session'; -const mapStateToProps = ({ searchBar, form }: RootState): SearchBarDataProps => { +const mapStateToProps = ({ searchBar, form , auth }: RootState): SearchBarDataProps => { return { searchValue: searchBar.searchValue, currentView: searchBar.currentView, @@ -33,7 +35,8 @@ const mapStateToProps = ({ searchBar, form }: RootState): SearchBarDataProps => tags: form[SEARCH_BAR_ADVANCED_FORM_NAME], saveQuery: form[SEARCH_BAR_ADVANCED_FORM_NAME] && form[SEARCH_BAR_ADVANCED_FORM_NAME].values && - form[SEARCH_BAR_ADVANCED_FORM_NAME].values!.saveQuery + form[SEARCH_BAR_ADVANCED_FORM_NAME].values!.saveQuery, + sessions: auth.sessions, }; }; @@ -51,7 +54,8 @@ const mapDispatchToProps = (dispatch: Dispatch): SearchBarActionProps => ({ editSavedQuery: (data: SearchBarAdvancedFormData) => dispatch(editSavedQuery(data)), moveUp: () => dispatch(moveUp()), moveDown: () => dispatch(moveDown()), - setAdvancedDataFromSearchValue: (search: string, vocabulary: Vocabulary) => dispatch(setAdvancedDataFromSearchValue(search, vocabulary)) + setAdvancedDataFromSearchValue: (search: string, vocabulary: Vocabulary) => dispatch(setAdvancedDataFromSearchValue(search, vocabulary)), + searchSingleCluster: (session: Session, searchValue: string) => {dispatch(searchSingleCluster(session, searchValue))}, }); export const SearchBar = connect(mapStateToProps, mapDispatchToProps)(SearchBarView); -- 2.30.2