From 5729de6b47d3e1bf40f4b2869ffcc6fbf88d0085 Mon Sep 17 00:00:00 2001 From: Peter Amstutz Date: Wed, 18 Oct 2023 17:40:46 -0400 Subject: [PATCH] 21077: First pass Arvados-DCO-1.1-Signed-off-by: Peter Amstutz --- src/index.tsx | 2 +- .../all-processes-panel-middleware-service.ts | 12 +- .../data-explorer/data-explorer-action.ts | 4 +- .../data-explorer-middleware-service.ts | 3 +- .../data-explorer/data-explorer-middleware.ts | 178 +++++++++--------- src/store/processes/process.ts | 12 +- .../project-panel-middleware-service.ts | 6 +- .../subprocess-panel-middleware-service.ts | 46 ++--- src/websocket/websocket.ts | 8 +- 9 files changed, 139 insertions(+), 132 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index d2af0952..646dfd4d 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -149,7 +149,7 @@ fetchConfig().then(({ config, apiHost }) => { const services = createServices(config, { progressFn: (id, working) => { - store.dispatch(progressIndicatorActions.TOGGLE_WORKING({ id, working })); + //store.dispatch(progressIndicatorActions.TOGGLE_WORKING({ id, working })); }, errorFn: (id, error, showSnackBar: boolean) => { if (showSnackBar) { diff --git a/src/store/all-processes-panel/all-processes-panel-middleware-service.ts b/src/store/all-processes-panel/all-processes-panel-middleware-service.ts index 227d2fa0..955d9689 100644 --- a/src/store/all-processes-panel/all-processes-panel-middleware-service.ts +++ b/src/store/all-processes-panel/all-processes-panel-middleware-service.ts @@ -27,13 +27,13 @@ export class AllProcessesPanelMiddlewareService extends DataExplorerMiddlewareSe super(id); } - async requestItems(api: MiddlewareAPI) { + async requestItems(api: MiddlewareAPI, criteriaChanged?: boolean, background?: boolean) { const dataExplorer = getDataExplorer(api.getState().dataExplorer, this.getId()); if (!dataExplorer) { api.dispatch(allProcessesPanelDataExplorerIsNotSet()); } else { try { - api.dispatch(progressIndicatorActions.START_WORKING(this.getId())); + if (!background) { api.dispatch(progressIndicatorActions.START_WORKING(this.getId())); } const processItems = await this.services.containerRequestService.list( { ...getParams(dataExplorer), @@ -41,7 +41,7 @@ export class AllProcessesPanelMiddlewareService extends DataExplorerMiddlewareSe select: containerRequestFieldsNoMounts, }); - api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); + if (!background) { api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); } api.dispatch(resourcesActions.SET_RESOURCES(processItems.items)); await api.dispatch(loadMissingProcessesInformation(processItems.items)); api.dispatch(allProcessesPanelActions.SET_ITEMS({ @@ -51,7 +51,7 @@ export class AllProcessesPanelMiddlewareService extends DataExplorerMiddlewareSe rowsPerPage: processItems.limit })); } catch { - api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); + if (!background) { api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); } api.dispatch(allProcessesPanelActions.SET_ITEMS({ items: [], itemsAvailable: 0, @@ -64,13 +64,13 @@ export class AllProcessesPanelMiddlewareService extends DataExplorerMiddlewareSe } } -const getParams = ( dataExplorer: DataExplorer ) => ({ +const getParams = (dataExplorer: DataExplorer) => ({ ...dataExplorerToListParams(dataExplorer), order: getOrder(dataExplorer), filters: getFilters(dataExplorer) }); -const getFilters = ( dataExplorer: DataExplorer ) => { +const getFilters = (dataExplorer: DataExplorer) => { const columns = dataExplorer.columns as DataColumns; const statusColumnFilters = getDataExplorerColumnFilters(columns, 'Status'); const activeStatusFilter = Object.keys(statusColumnFilters).find( diff --git a/src/store/data-explorer/data-explorer-action.ts b/src/store/data-explorer/data-explorer-action.ts index f686c939..ea050e60 100644 --- a/src/store/data-explorer/data-explorer-action.ts +++ b/src/store/data-explorer/data-explorer-action.ts @@ -15,7 +15,7 @@ export enum DataTableRequestState { export const dataExplorerActions = unionize({ CLEAR: ofType<{ id: string }>(), RESET_PAGINATION: ofType<{ id: string }>(), - REQUEST_ITEMS: ofType<{ id: string; criteriaChanged?: boolean }>(), + REQUEST_ITEMS: ofType<{ id: string; criteriaChanged?: boolean, background?: boolean }>(), REQUEST_STATE: ofType<{ id: string; criteriaChanged?: boolean }>(), SET_FETCH_MODE: ofType<{ id: string; fetchMode: DataTableFetchMode }>(), SET_COLUMNS: ofType<{ id: string; columns: DataColumns }>(), @@ -36,7 +36,7 @@ export type DataExplorerAction = UnionOf; export const bindDataExplorerActions = (id: string) => ({ CLEAR: () => dataExplorerActions.CLEAR({ id }), RESET_PAGINATION: () => dataExplorerActions.RESET_PAGINATION({ id }), - REQUEST_ITEMS: (criteriaChanged?: boolean) => dataExplorerActions.REQUEST_ITEMS({ id, criteriaChanged }), + REQUEST_ITEMS: (criteriaChanged?: boolean, background?: boolean) => dataExplorerActions.REQUEST_ITEMS({ id, criteriaChanged, background }), SET_FETCH_MODE: (payload: { fetchMode: DataTableFetchMode }) => dataExplorerActions.SET_FETCH_MODE({ ...payload, id }), SET_COLUMNS: (payload: { columns: DataColumns }) => dataExplorerActions.SET_COLUMNS({ ...payload, id }), SET_FILTERS: (payload: { columnName: string; filters: DataTableFilters }) => dataExplorerActions.SET_FILTERS({ ...payload, id }), diff --git a/src/store/data-explorer/data-explorer-middleware-service.ts b/src/store/data-explorer/data-explorer-middleware-service.ts index 01964fa4..e424b5e8 100644 --- a/src/store/data-explorer/data-explorer-middleware-service.ts +++ b/src/store/data-explorer/data-explorer-middleware-service.ts @@ -33,7 +33,8 @@ export abstract class DataExplorerMiddlewareService { abstract requestItems( api: MiddlewareAPI, - criteriaChanged?: boolean + criteriaChanged?: boolean, + background?: boolean ): Promise; } diff --git a/src/store/data-explorer/data-explorer-middleware.ts b/src/store/data-explorer/data-explorer-middleware.ts index f83b0646..3404b375 100644 --- a/src/store/data-explorer/data-explorer-middleware.ts +++ b/src/store/data-explorer/data-explorer-middleware.ts @@ -16,98 +16,98 @@ import { DataExplorerMiddlewareService } from './data-explorer-middleware-servic export const dataExplorerMiddleware = (service: DataExplorerMiddlewareService): Middleware => - (api) => - (next) => { - const actions = bindDataExplorerActions(service.getId()); + (api) => + (next) => { + const actions = bindDataExplorerActions(service.getId()); - return (action) => { - const handleAction = - (handler: (data: T) => void) => - (data: T) => { - next(action); - if (data.id === service.getId()) { - handler(data); - } - }; - dataExplorerActions.match(action, { - SET_PAGE: handleAction(() => { - api.dispatch(actions.REQUEST_ITEMS(false)); - }), - SET_ROWS_PER_PAGE: handleAction(() => { - api.dispatch(actions.REQUEST_ITEMS(true)); - }), - SET_FILTERS: handleAction(() => { - api.dispatch(actions.RESET_PAGINATION()); - api.dispatch(actions.REQUEST_ITEMS(true)); - }), - TOGGLE_SORT: handleAction(() => { - api.dispatch(actions.REQUEST_ITEMS(true)); - }), - SET_EXPLORER_SEARCH_VALUE: handleAction(() => { - api.dispatch(actions.RESET_PAGINATION()); - api.dispatch(actions.REQUEST_ITEMS(true)); - }), - REQUEST_ITEMS: handleAction(({ criteriaChanged }) => { - api.dispatch(async ( - dispatch: Dispatch, - getState: () => RootState, - services: ServiceRepository - ) => { - while (true) { - let de = getDataExplorer( - getState().dataExplorer, - service.getId() - ); - switch (de.requestState) { - case DataTableRequestState.IDLE: - // Start a new request. - try { - dispatch( - actions.SET_REQUEST_STATE({ - requestState: DataTableRequestState.PENDING, - }) - ); - await service.requestItems(api, criteriaChanged); - } catch { - dispatch( - actions.SET_REQUEST_STATE({ - requestState: DataTableRequestState.NEED_REFRESH, - }) - ); - } - // Now check if the state is still PENDING, if it moved to NEED_REFRESH - // then we need to reissue requestItems - de = getDataExplorer( + return (action) => { + const handleAction = + (handler: (data: T) => void) => + (data: T) => { + next(action); + if (data.id === service.getId()) { + handler(data); + } + }; + dataExplorerActions.match(action, { + SET_PAGE: handleAction(() => { + api.dispatch(actions.REQUEST_ITEMS(false)); + }), + SET_ROWS_PER_PAGE: handleAction(() => { + api.dispatch(actions.REQUEST_ITEMS(true)); + }), + SET_FILTERS: handleAction(() => { + api.dispatch(actions.RESET_PAGINATION()); + api.dispatch(actions.REQUEST_ITEMS(true)); + }), + TOGGLE_SORT: handleAction(() => { + api.dispatch(actions.REQUEST_ITEMS(true)); + }), + SET_EXPLORER_SEARCH_VALUE: handleAction(() => { + api.dispatch(actions.RESET_PAGINATION()); + api.dispatch(actions.REQUEST_ITEMS(true)); + }), + REQUEST_ITEMS: handleAction(({ criteriaChanged, background }) => { + api.dispatch(async ( + dispatch: Dispatch, + getState: () => RootState, + services: ServiceRepository + ) => { + while (true) { + let de = getDataExplorer( getState().dataExplorer, service.getId() ); - const complete = - de.requestState === DataTableRequestState.PENDING; - dispatch( - actions.SET_REQUEST_STATE({ - requestState: DataTableRequestState.IDLE, - }) - ); - if (complete) { - return; + switch (de.requestState) { + case DataTableRequestState.IDLE: + // Start a new request. + try { + dispatch( + actions.SET_REQUEST_STATE({ + requestState: DataTableRequestState.PENDING, + }) + ); + await service.requestItems(api, criteriaChanged, background); + } catch { + dispatch( + actions.SET_REQUEST_STATE({ + requestState: DataTableRequestState.NEED_REFRESH, + }) + ); + } + // Now check if the state is still PENDING, if it moved to NEED_REFRESH + // then we need to reissue requestItems + de = getDataExplorer( + getState().dataExplorer, + service.getId() + ); + const complete = + de.requestState === DataTableRequestState.PENDING; + dispatch( + actions.SET_REQUEST_STATE({ + requestState: DataTableRequestState.IDLE, + }) + ); + if (complete) { + return; + } + break; + case DataTableRequestState.PENDING: + // State is PENDING, move it to NEED_REFRESH so that when the current request finishes it starts a new one. + dispatch( + actions.SET_REQUEST_STATE({ + requestState: DataTableRequestState.NEED_REFRESH, + }) + ); + return; + case DataTableRequestState.NEED_REFRESH: + // Nothing to do right now. + return; } - break; - case DataTableRequestState.PENDING: - // State is PENDING, move it to NEED_REFRESH so that when the current request finishes it starts a new one. - dispatch( - actions.SET_REQUEST_STATE({ - requestState: DataTableRequestState.NEED_REFRESH, - }) - ); - return; - case DataTableRequestState.NEED_REFRESH: - // Nothing to do right now. - return; - } - } + } + }); + }), + default: () => next(action), }); - }), - default: () => next(action), - }); - }; - }; + }; + }; diff --git a/src/store/processes/process.ts b/src/store/processes/process.ts index 526629cd..1063d0bd 100644 --- a/src/store/processes/process.ts +++ b/src/store/processes/process.ts @@ -129,15 +129,21 @@ export const getProcessStatus = ({ containerRequest, container }: Process): Proc case containerRequest.containerUuid && !container: return ProcessStatus.UNKNOWN; + case containerRequest.state === ContainerRequestState.UNCOMMITTED: + return ProcessStatus.DRAFT; + + case containerRequest.state === ContainerRequestState.FINAL && + container?.state === ContainerState.RUNNING: + // It's right about to be completed but we haven't + // gotten the updated container record yet + return ProcessStatus.RUNNING; + case containerRequest.state === ContainerRequestState.FINAL && container?.state !== ContainerState.COMPLETE: // Request was finalized before its container started (or the // container was cancelled) return ProcessStatus.CANCELLED; - case containerRequest.state === ContainerRequestState.UNCOMMITTED: - return ProcessStatus.DRAFT; - case container && container.state === ContainerState.COMPLETE: if (container?.exitCode === 0) { if (containerRequest && container.finishedAt) { diff --git a/src/store/project-panel/project-panel-middleware-service.ts b/src/store/project-panel/project-panel-middleware-service.ts index 7204993b..09b39afb 100644 --- a/src/store/project-panel/project-panel-middleware-service.ts +++ b/src/store/project-panel/project-panel-middleware-service.ts @@ -41,7 +41,7 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService super(id); } - async requestItems(api: MiddlewareAPI) { + async requestItems(api: MiddlewareAPI, criteriaChanged?: boolean, background?: boolean) { const state = api.getState(); const dataExplorer = getDataExplorer(state.dataExplorer, this.getId()); const projectUuid = getProjectPanelCurrentUuid(state); @@ -52,7 +52,7 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService api.dispatch(projectPanelDataExplorerIsNotSet()); } else { try { - api.dispatch(progressIndicatorActions.START_WORKING(this.getId())); + if (!background) { api.dispatch(progressIndicatorActions.START_WORKING(this.getId())); } const response = await this.services.groupsService.contents(projectUuid, getParams(dataExplorer, !!isProjectTrashed)); const resourceUuids = response.items.map(item => item.uuid); api.dispatch(updateFavorites(resourceUuids)); @@ -71,7 +71,7 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService ); api.dispatch(couldNotFetchProjectContents()); } finally { - api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); + if (!background) { api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); } } } } diff --git a/src/store/subprocess-panel/subprocess-panel-middleware-service.ts b/src/store/subprocess-panel/subprocess-panel-middleware-service.ts index 986c6ebd..5124c834 100644 --- a/src/store/subprocess-panel/subprocess-panel-middleware-service.ts +++ b/src/store/subprocess-panel/subprocess-panel-middleware-service.ts @@ -26,19 +26,19 @@ export class SubprocessMiddlewareService extends DataExplorerMiddlewareService { super(id); } - async requestItems(api: MiddlewareAPI) { + async requestItems(api: MiddlewareAPI, criteriaChanged?: boolean, background?: boolean) { const state = api.getState(); const parentContainerRequestUuid = state.processPanel.containerRequestUuid; if (parentContainerRequestUuid === "") { return; } const dataExplorer = getDataExplorer(state.dataExplorer, this.getId()); try { - api.dispatch(progressIndicatorActions.START_WORKING(this.getId())); + if (!background) { api.dispatch(progressIndicatorActions.START_WORKING(this.getId())); } const parentContainerRequest = await this.services.containerRequestService.get(parentContainerRequestUuid); if (parentContainerRequest.containerUuid) { const containerRequests = await this.services.containerRequestService.list( { - ...getParams(dataExplorer, parentContainerRequest) , + ...getParams(dataExplorer, parentContainerRequest), select: containerRequestFieldsNoMounts }); api.dispatch(updateResources(containerRequests.items)); @@ -46,9 +46,9 @@ export class SubprocessMiddlewareService extends DataExplorerMiddlewareService { // Populate the actual user view api.dispatch(setItems(containerRequests)); } - api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); + if (!background) { api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); } } catch { - api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); + if (!background) { api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId())); } api.dispatch(couldNotFetchSubprocesses()); } } @@ -65,27 +65,27 @@ export const getParams = ( export const getFilters = ( dataExplorer: DataExplorer, parentContainerRequest: ContainerRequestResource) => { - const columns = dataExplorer.columns as DataColumns; - const statusColumnFilters = getDataExplorerColumnFilters(columns, 'Status'); - const activeStatusFilter = Object.keys(statusColumnFilters).find( - filterName => statusColumnFilters[filterName].selected - ) || ProcessStatusFilter.ALL; + const columns = dataExplorer.columns as DataColumns; + const statusColumnFilters = getDataExplorerColumnFilters(columns, 'Status'); + const activeStatusFilter = Object.keys(statusColumnFilters).find( + filterName => statusColumnFilters[filterName].selected + ) || ProcessStatusFilter.ALL; - // Get all the subprocess' container requests and containers. - const fb = new FilterBuilder().addEqual('requesting_container_uuid', parentContainerRequest.containerUuid); - const statusFilters = buildProcessStatusFilters(fb, activeStatusFilter).getFilters(); + // Get all the subprocess' container requests and containers. + const fb = new FilterBuilder().addEqual('requesting_container_uuid', parentContainerRequest.containerUuid); + const statusFilters = buildProcessStatusFilters(fb, activeStatusFilter).getFilters(); - const nameFilters = dataExplorer.searchValue - ? new FilterBuilder() - .addILike("name", dataExplorer.searchValue) - .getFilters() - : ''; + const nameFilters = dataExplorer.searchValue + ? new FilterBuilder() + .addILike("name", dataExplorer.searchValue) + .getFilters() + : ''; - return joinFilters( - nameFilters, - statusFilters - ); - }; + return joinFilters( + nameFilters, + statusFilters + ); +}; export const setItems = (listResults: ListResults) => subprocessPanelActions.SET_ITEMS({ diff --git a/src/websocket/websocket.ts b/src/websocket/websocket.ts index a7be6ba1..1b74b11f 100644 --- a/src/websocket/websocket.ts +++ b/src/websocket/websocket.ts @@ -46,7 +46,7 @@ const messageListener = (store: RootStore) => (message: ResourceEventMessage) => } const proc = getProcess(state.processPanel.containerRequestUuid)(state.resources); if (proc && proc.container && proc.container.uuid === message.properties["new_attributes"]["requesting_container_uuid"]) { - store.dispatch(subprocessPanelActions.REQUEST_ITEMS()); + store.dispatch(subprocessPanelActions.REQUEST_ITEMS(false, true)); return; } } @@ -57,16 +57,16 @@ const messageListener = (store: RootStore) => (message: ResourceEventMessage) => const subproc = getSubprocesses(state.processPanel.containerRequestUuid)(state.resources); for (const sb of subproc) { if (sb.containerRequest.uuid === message.objectUuid || (sb.container && sb.container.uuid === message.objectUuid)) { - store.dispatch(subprocessPanelActions.REQUEST_ITEMS()); + store.dispatch(subprocessPanelActions.REQUEST_ITEMS(false, true)); break; } } } if (matchAllProcessesRoute(location)) { - store.dispatch(allProcessesPanelActions.REQUEST_ITEMS()); + store.dispatch(allProcessesPanelActions.REQUEST_ITEMS(false, true)); } if (matchProjectRoute(location) && message.objectOwnerUuid === getProjectPanelCurrentUuid(state)) { - store.dispatch(projectPanelActions.REQUEST_ITEMS()); + store.dispatch(projectPanelActions.REQUEST_ITEMS(false, true)); } return; default: -- 2.30.2