From e1a2f656d495eb58260eb32b1cbe6e51cd959fc9 Mon Sep 17 00:00:00 2001 From: Arsen Alkamyan <44676488+arsengit@users.noreply.github.com> Date: Thu, 7 Jul 2022 23:17:59 +0400 Subject: [PATCH] [fix] Avoid saving crashed or terminated search requests as the last state on explorers (#1950) * [fix] Avoid saving crashed and aborted queries * [fix] Remove referring to the explorer docs after getting a syntax error --- CHANGELOG.md | 1 + .../AutocompleteInput/ AutocompleteInput.d.ts | 1 - .../AutocompleteInput/AutocompleteInput.tsx | 18 ++------- .../components/SelectForm/SelectForm.tsx | 30 ++++++++------- .../components/SelectForm/SelectForm.tsx | 28 ++++++++------ .../components/SelectForm/SelectForm.tsx | 33 +++++++++------- .../Runs/components/SearchBar/SearchBar.tsx | 2 - .../components/SelectForm/SelectForm.tsx | 2 - .../models/explorer/createAppModel.ts | 27 ++++++------- .../ui/src/services/models/explorer/index.ts | 8 ---- .../imagesExplore/imagesExploreAppModel.ts | 38 +++++++++++-------- .../imagesExplore/imagesExploreAppModel.d.ts | 2 + .../src/utils/app/getQueryStringFromSelect.ts | 8 +++- .../src/utils/app/toggleSelectAdvancedMode.ts | 16 ++++---- aim/web/ui/src/utils/encoder/encoder.ts | 2 +- 15 files changed, 108 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be388ec952..c5a6600170 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixes: +- Avoid saving crashed or terminated search requests as the last state on explorers (arsengit) - Remove the progress bar blinking when searching runs in Runs Explorer (KaroMourad) - Fix the "matched runs" sentence color style in progress bars (KaroMourad) - Fix `SyntaxError` handling for python3.10+ (mihran113) diff --git a/aim/web/ui/src/components/AutocompleteInput/ AutocompleteInput.d.ts b/aim/web/ui/src/components/AutocompleteInput/ AutocompleteInput.d.ts index f74f70081f..b295bad786 100644 --- a/aim/web/ui/src/components/AutocompleteInput/ AutocompleteInput.d.ts +++ b/aim/web/ui/src/components/AutocompleteInput/ AutocompleteInput.d.ts @@ -15,7 +15,6 @@ export interface IAutocompleteInputProps { disabled?: boolean; value: string | undefined; refObject?: React.MutableRefObject; - appName?: AppNameEnumUpperCase; getEditorValue?: (value: string) => string; onEnter?: () => void; onChange?: ( diff --git a/aim/web/ui/src/components/AutocompleteInput/AutocompleteInput.tsx b/aim/web/ui/src/components/AutocompleteInput/AutocompleteInput.tsx index 1dba5caea5..8c602990ed 100644 --- a/aim/web/ui/src/components/AutocompleteInput/AutocompleteInput.tsx +++ b/aim/web/ui/src/components/AutocompleteInput/AutocompleteInput.tsx @@ -10,8 +10,6 @@ import { Icon, Text } from 'components/kit'; import { getMonacoConfig } from 'config/monacoConfig/monacoConfig'; import { DOCUMENTATIONS } from 'config/references'; -import { AppNameEnumUpperCase } from 'services/models/explorer'; - import { showAutocompletion } from 'utils/showAutocompletion'; import { IAutocompleteInputProps } from './ AutocompleteInput'; @@ -27,7 +25,6 @@ function AutocompleteInput({ refObject, error, disabled = false, - appName = AppNameEnumUpperCase.METRICS, //callback functions onEnter, onChange, @@ -248,20 +245,11 @@ function AutocompleteInput({
- Aim Query Language is pythonic and fairly easy to get used to. If - you having issue, please refer to the{' '} - + If you are having issues, please refer to the{' '} + docs {' '} - for more examples or the detailed{' '} - - spec - - . + for detailed usage guide and more examples.
diff --git a/aim/web/ui/src/pages/ImagesExplore/components/SelectForm/SelectForm.tsx b/aim/web/ui/src/pages/ImagesExplore/components/SelectForm/SelectForm.tsx index d9bbd54b93..d1f56fa6ba 100644 --- a/aim/web/ui/src/pages/ImagesExplore/components/SelectForm/SelectForm.tsx +++ b/aim/web/ui/src/pages/ImagesExplore/components/SelectForm/SelectForm.tsx @@ -23,7 +23,6 @@ import { ANALYTICS_EVENT_KEYS } from 'config/analytics/analyticsKeysMap'; import imagesExploreAppModel from 'services/models/imagesExplore/imagesExploreAppModel'; import { trackEvent } from 'services/analytics'; -import { AppNameEnumUpperCase } from 'services/models/explorer'; import { ISelectFormProps } from 'types/pages/imagesExplore/components/SelectForm/SelectForm'; import { ISelectOption } from 'types/services/models/explorer/createAppModel'; @@ -86,17 +85,22 @@ function SelectForm({ imagesExploreAppModel.abortRequest(); } - function onSelect(event: object, value: ISelectOption[]): void { - const lookup = value.reduce( - (acc: { [key: string]: number }, curr: ISelectOption) => { - acc[curr.label] = ++acc[curr.label] || 0; - return acc; - }, - {}, - ); - onImagesExploreSelectChange( - value.filter((option: ISelectOption) => lookup[option.label] === 0), - ); + function onSelect( + event: React.ChangeEvent<{}>, + value: ISelectOption[], + ): void { + if (event.type === 'click') { + const lookup = value.reduce( + (acc: { [key: string]: number }, curr: ISelectOption) => { + acc[curr.label] = ++acc[curr.label] || 0; + return acc; + }, + {}, + ); + onImagesExploreSelectChange( + value.filter((option: ISelectOption) => lookup[option.label] === 0), + ); + } } function handleDelete(field: string): void { @@ -163,7 +167,6 @@ function SelectForm({ { - acc[curr.label] = ++acc[curr.label] || 0; - return acc; - }, - {}, - ); - onMetricsSelectChange(value.filter((option) => lookup[option.label] === 0)); + function onSelect( + event: React.ChangeEvent<{}>, + value: ISelectOption[], + ): void { + if (event.type === 'click') { + const lookup = value.reduce( + (acc: { [key: string]: number }, curr: ISelectOption) => { + acc[curr.label] = ++acc[curr.label] || 0; + return acc; + }, + {}, + ); + onMetricsSelectChange( + value.filter((option) => lookup[option.label] === 0), + ); + } } function handleDelete(field: string): void { @@ -153,7 +159,6 @@ function SelectForm({ { - acc[curr.label] = ++acc[curr.label] || 0; - return acc; - }, - {}, - ); - onParamsSelectChange( - value?.filter((option: ISelectOption) => lookup[option.label] === 0), - ); + function onSelect( + event: React.ChangeEvent<{}>, + value: ISelectOption[], + ): void { + if (event.type === 'click') { + const lookup = value.reduce( + (acc: { [key: string]: number }, curr: ISelectOption) => { + acc[curr.label] = ++acc[curr.label] || 0; + return acc; + }, + {}, + ); + onParamsSelectChange( + value?.filter((option: ISelectOption) => lookup[option.label] === 0), + ); + } } function handleDelete(field: string): void { @@ -99,6 +103,8 @@ function SelectForm({ function handleSearchInputChange( e: React.ChangeEvent, ): void { + e.preventDefault(); + e.stopPropagation(); setSearchValue(e.target.value); } @@ -112,7 +118,7 @@ function SelectForm({ const open: boolean = !!anchorEl; const id = open ? 'select-metric' : undefined; - + console.log(selectedParamsData); return (
@@ -270,7 +276,6 @@ function SelectForm({
setRequestProgress(model, progress), ); + if (shouldUrlUpdate) { + updateURL({ configData, appName }); + } updateData(runData); } catch (ex: Error | any) { if (ex.name === 'AbortError') { @@ -2226,9 +2227,6 @@ function createAppModel(appConfig: IAppInitialConfig) { runsRequestRef = runsService.getRunsData(query, 45, pagination?.offset); let limit = pagination.limit; - if (shouldUrlUpdate) { - updateURL({ configData, appName }); - } setRequestProgress(model); return { call: async () => { @@ -2303,6 +2301,9 @@ function createAppModel(appConfig: IAppInitialConfig) { }, }, }); + if (shouldUrlUpdate) { + updateURL({ configData, appName }); + } } catch (ex: Error | any) { if (ex.name === 'AbortError') { onNotificationAdd({ @@ -3351,9 +3352,6 @@ function createAppModel(appConfig: IAppInitialConfig) { if (queryString) { configData.select.query = queryString; } - if (shouldUrlUpdate) { - updateURL({ configData, appName }); - } runsRequestRef = runsService.getRunsData(configData?.select?.query); setRequestProgress(model); return { @@ -3378,6 +3376,9 @@ function createAppModel(appConfig: IAppInitialConfig) { setRequestProgress(model, progress), ); updateData(runData); + if (shouldUrlUpdate) { + updateURL({ configData, appName }); + } } catch (ex: Error | any) { if (ex.name === 'AbortError') { // Abort Error @@ -5652,9 +5653,7 @@ function createAppModel(appConfig: IAppInitialConfig) { runsRequestRef.abort(); } const configData = { ...model.getState()?.config }; - if (shouldUrlUpdate) { - updateURL({ configData, appName }); - } + runsRequestRef = runsService.getRunsData(configData?.select?.query); setRequestProgress(model); return { @@ -5679,7 +5678,9 @@ function createAppModel(appConfig: IAppInitialConfig) { setRequestProgress(model, progress), ); updateData(runData); - + if (shouldUrlUpdate) { + updateURL({ configData, appName }); + } liveUpdateInstance?.start({ q: configData?.select?.query, }); diff --git a/aim/web/ui/src/services/models/explorer/index.ts b/aim/web/ui/src/services/models/explorer/index.ts index 27a92f336b..29c6bdb950 100644 --- a/aim/web/ui/src/services/models/explorer/index.ts +++ b/aim/web/ui/src/services/models/explorer/index.ts @@ -22,14 +22,6 @@ export enum AppNameEnum { SCATTERS = 'scatters', } -export enum AppNameEnumUpperCase { - METRICS = 'METRICS', - PARAMS = 'PARAMS', - RUNS = 'RUNS', - IMAGES = 'IMAGES', - SCATTERS = 'SCATTERS', -} - /** * appInitialConfig is config object which describes our app models * @appInitialConfig { [key: string]: IAppInitialConfig } diff --git a/aim/web/ui/src/services/models/imagesExplore/imagesExploreAppModel.ts b/aim/web/ui/src/services/models/imagesExplore/imagesExploreAppModel.ts index 4537c47248..c6fd0bf7a5 100644 --- a/aim/web/ui/src/services/models/imagesExplore/imagesExploreAppModel.ts +++ b/aim/web/ui/src/services/models/imagesExplore/imagesExploreAppModel.ts @@ -53,6 +53,7 @@ import { ISelectOption, } from 'types/services/models/explorer/createAppModel'; import { IProjectParamsMetrics } from 'types/services/models/projects/projectsModel'; +import { ISyntaxErrorDetails } from 'types/components/NotificationContainer/NotificationContainer'; import getAppConfigDataMethod from 'utils/app/getAppConfigData'; import onRowSelectAction from 'utils/app/onRowSelect'; @@ -99,6 +100,8 @@ const model = createModel>({ selectFormData: { options: undefined, suggestions: [], + error: null, + advancedError: null, }, config: getConfig(), }); @@ -339,9 +342,6 @@ function getImagesData( configData!.select.query = queryString; } } - if (shouldUrlUpdate) { - updateURL(configData); - } const recordSlice: number[] | undefined = configData?.images?.recordSlice as | number[] | undefined; @@ -350,7 +350,7 @@ function getImagesData( | undefined; const recordDensity = configData?.images?.recordDensity; const indexDensity = configData?.images?.indexDensity; - let query = getQueryStringFromSelect(configData?.select as any); + let query = getQueryStringFromSelect(configData!.select); let imageDataBody: any = { q: query !== '()' ? query : '', }; @@ -402,6 +402,9 @@ function getImagesData( if (configData) { setModelData(runData, configData); } + if (shouldUrlUpdate) { + updateURL(configData); + } } catch (ex: Error | any) { if (ex.name === 'AbortError') { // Abort Error @@ -736,7 +739,7 @@ function setModelData(rawData: any[], configData: IImagesExploreAppConfig) { config, params, selectFormData: { - ...modelState.selectFormData, + ...modelState?.selectFormData, [configData.select?.advancedMode ? 'advancedError' : 'error']: null, }, data, @@ -1774,7 +1777,7 @@ function onSelectRunQueryChange(query: string) { if (configData?.select) { const newConfig = { ...configData, - select: { ...configData.select, advancedQuery: query, query }, + select: { ...configData.select, query }, images: { ...configData.images }, }; @@ -1822,6 +1825,7 @@ function onSearchQueryCopy(): void { function getQueryStringFromSelect( selectData: IImagesExploreAppConfig['select'], + error?: ISyntaxErrorDetails, ) { let query: string | undefined = ''; if (selectData !== undefined) { @@ -1829,7 +1833,7 @@ function getQueryStringFromSelect( query = selectData.advancedQuery; } else { query = `${ - selectData.query ? `(${selectData.query}) and ` : '' + selectData.query && !error?.message ? `(${selectData.query}) and ` : '' }(${selectData.options .map( (option: ISelectOption) => @@ -1887,22 +1891,24 @@ function onImagesExploreSelectChange(options: ISelectOption[]) { } function toggleSelectAdvancedMode() { - const configData: IImagesExploreAppConfig | undefined = - model.getState()?.config; + const modelState: IImagesExploreAppModelState | any = model.getState(); - if (configData?.select) { + if (modelState.config?.select) { let query = - configData.select.advancedQuery || - getQueryStringFromSelect(configData?.select); + modelState.config.select.advancedQuery || + getQueryStringFromSelect( + modelState.config?.select, + modelState.selectFormData.error, + ); if (query === '()') { query = ''; } const newConfig = { - ...configData, + ...modelState.config, select: { - ...configData.select, + ...modelState.config.select, advancedQuery: query, - advancedMode: !configData.select.advancedMode, + advancedMode: !modelState.config.select.advancedMode, }, }; updateURL(newConfig); @@ -1912,7 +1918,7 @@ function toggleSelectAdvancedMode() { analytics.trackEvent( `${ANALYTICS_EVENT_KEYS.images.useAdvancedSearch} ${ - !configData?.select.advancedMode ? 'on' : 'off' + !modelState.config?.select.advancedMode ? 'on' : 'off' }`, ); } diff --git a/aim/web/ui/src/types/services/models/imagesExplore/imagesExploreAppModel.d.ts b/aim/web/ui/src/types/services/models/imagesExplore/imagesExploreAppModel.d.ts index 65d5f24b85..7538d39e3c 100644 --- a/aim/web/ui/src/types/services/models/imagesExplore/imagesExploreAppModel.d.ts +++ b/aim/web/ui/src/types/services/models/imagesExplore/imagesExploreAppModel.d.ts @@ -79,6 +79,8 @@ export interface IImagesExploreAppModelState { selectFormData: { options: ISelectOption[] | undefined; suggestions: string[]; + error: ISyntaxErrorDetails; + advancedError: ISyntaxErrorDetails; }; selectedRows: { [key: string]: any }; // liveUpdateConfig: { diff --git a/aim/web/ui/src/utils/app/getQueryStringFromSelect.ts b/aim/web/ui/src/utils/app/getQueryStringFromSelect.ts index 6f1371f630..164dfbed8d 100644 --- a/aim/web/ui/src/utils/app/getQueryStringFromSelect.ts +++ b/aim/web/ui/src/utils/app/getQueryStringFromSelect.ts @@ -1,15 +1,19 @@ +import { ISyntaxErrorDetails } from 'types/components/NotificationContainer/NotificationContainer'; import { ISelectConfig } from 'types/services/models/explorer/createAppModel'; import { formatValue } from '../formatValue'; -export default function getQueryStringFromSelect(selectData: ISelectConfig) { +export default function getQueryStringFromSelect( + selectData: ISelectConfig, + error?: ISyntaxErrorDetails, +) { let query = ''; if (selectData !== undefined) { if (selectData.advancedMode) { query = selectData.advancedQuery || ''; } else { query = `${ - selectData.query ? `(${selectData.query}) and ` : '' + selectData.query && !error?.message ? `(${selectData.query}) and ` : '' }(${selectData.options .map( (option) => diff --git a/aim/web/ui/src/utils/app/toggleSelectAdvancedMode.ts b/aim/web/ui/src/utils/app/toggleSelectAdvancedMode.ts index ca304e9348..6860d4e142 100644 --- a/aim/web/ui/src/utils/app/toggleSelectAdvancedMode.ts +++ b/aim/web/ui/src/utils/app/toggleSelectAdvancedMode.ts @@ -13,20 +13,20 @@ export default function toggleSelectAdvancedMode({ model: IModel; appName: string; }): void { - const configData = model.getState()?.config; - if (configData?.select) { + const { config, selectFormData } = model.getState(); + if (config) { let query = - configData.select.advancedQuery || - getQueryStringFromSelect(configData?.select); + config.select.advancedQuery || + getQueryStringFromSelect(config?.select, selectFormData.error); if (query === '()') { query = ''; } const newConfig = { - ...configData, + ...config, select: { - ...configData.select, + ...config.select, advancedQuery: query, - advancedMode: !configData.select.advancedMode, + advancedMode: !config.select.advancedMode, }, }; @@ -35,7 +35,7 @@ export default function toggleSelectAdvancedMode({ analytics.trackEvent( // @ts-ignore `${ANALYTICS_EVENT_KEYS[appName].useAdvancedSearch} ${ - !configData?.select.advancedMode ? 'on' : 'off' + !config?.select.advancedMode ? 'on' : 'off' }`, ); } diff --git a/aim/web/ui/src/utils/encoder/encoder.ts b/aim/web/ui/src/utils/encoder/encoder.ts index 1616a2361c..0d4ab755d7 100644 --- a/aim/web/ui/src/utils/encoder/encoder.ts +++ b/aim/web/ui/src/utils/encoder/encoder.ts @@ -14,7 +14,7 @@ const BS64_REPLACE_CHARACTERS = { }; // replace URL and CSS selectors, vulnerable characters '+', '/', '=', -const BS64_ENCODING_PADDING = ['', '=', '==', '===']; +const BS64_ENCODING_PADDING = ['', '===', '==', '=']; export const AIM64_ENCODING_PREFIX = 'O-'; // `O` - is a character which cannot exist in the base58-encoded data,