Skip to content

Commit

Permalink
fix(event-templates): should update template selection when template …
Browse files Browse the repository at this point in the history
…list updates (#1003) (#1007)

(cherry picked from commit a687e9f)

Co-authored-by: Thuan Vo <thvo@redhat.com>
  • Loading branch information
mergify[bot] and tthvo authored May 2, 2023
1 parent fb9f683 commit 7077cf1
Show file tree
Hide file tree
Showing 10 changed files with 156 additions and 98 deletions.
65 changes: 37 additions & 28 deletions src/app/CreateRecording/CustomRecordingForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ import { NotificationsContext } from '@app/Notifications/Notifications';
import { RecordingLabel } from '@app/RecordingMetadata/RecordingLabel';
import { RecordingLabelFields } from '@app/RecordingMetadata/RecordingLabelFields';
import { LoadingPropsType } from '@app/Shared/ProgressIndicator';
import { SelectTemplateSelectorForm } from '@app/Shared/SelectTemplateSelectorForm';
import { RecordingOptions, RecordingAttributes, TemplateType } from '@app/Shared/Services/Api.service';
import { ServiceContext } from '@app/Shared/Services/Services';
import { isTargetAgentHttp, NO_TARGET, Target } from '@app/Shared/Services/Target.service';
import { SelectTemplateSelectorForm } from '@app/TemplateSelector/SelectTemplateSelectorForm';
import { useSubscriptions } from '@app/utils/useSubscriptions';
import { portalRoot } from '@app/utils/utils';
import {
Expand Down Expand Up @@ -110,9 +110,8 @@ export const CustomRecordingForm: React.FC<CustomRecordingFormProps> = ({ prefil
const [duration, setDuration] = React.useState(prefilled?.duration || 30);
const [durationUnit, setDurationUnit] = React.useState(1000);
const [durationValid, setDurationValid] = React.useState(ValidatedOptions.success);
const [templates, setTemplates] = React.useState([] as EventTemplate[]);
const [templateName, setTemplateName] = React.useState<string | undefined>(prefilled?.templateName);
const [templateType, setTemplateType] = React.useState<TemplateType | undefined>(prefilled?.templateType);
const [templates, setTemplates] = React.useState<EventTemplate[]>([]);
const [template, setTemplate] = React.useState<Pick<Partial<EventTemplate>, 'name' | 'type'>>({});
const [maxAge, setMaxAge] = React.useState(prefilled?.maxAge || 0);
const [maxAgeUnits, setMaxAgeUnits] = React.useState(1);
const [maxSize, setMaxSize] = React.useState(prefilled?.maxSize || 0);
Expand Down Expand Up @@ -174,22 +173,25 @@ export const CustomRecordingForm: React.FC<CustomRecordingFormProps> = ({ prefil

const handleTemplateChange = React.useCallback(
(templateName?: string, templateType?: TemplateType) => {
setTemplateName(templateName);
setTemplateType(templateType);
setTemplate({
name: templateName,
type: templateType,
});
},
[setTemplateName, setTemplateType]
[setTemplate]
);

const getEventString = React.useCallback(() => {
const eventSpecifierString = React.useMemo(() => {
let str = '';
if (templateName) {
str += `template=${templateName}`;
const { name, type } = template;
if (name) {
str += `template=${name}`;
}
if (templateType) {
str += `,type=${templateType}`;
if (type) {
str += `,type=${type}`;
}
return str;
}, [templateName, templateType]);
}, [template]);

const getFormattedLabels = React.useCallback(() => {
const obj = {};
Expand Down Expand Up @@ -277,15 +279,15 @@ export const CustomRecordingForm: React.FC<CustomRecordingFormProps> = ({ prefil
};
const recordingAttributes: RecordingAttributes = {
name: recordingName,
events: getEventString(),
events: eventSpecifierString,
duration: continuous ? undefined : duration * (durationUnit / 1000),
archiveOnStop: archiveOnStop && !continuous,
options: options,
metadata: { labels: getFormattedLabels() },
};
handleCreateRecording(recordingAttributes);
}, [
getEventString,
eventSpecifierString,
getFormattedLabels,
archiveOnStop,
continuous,
Expand Down Expand Up @@ -315,31 +317,37 @@ export const CustomRecordingForm: React.FC<CustomRecordingFormProps> = ({ prefil
`targets/${encodeURIComponent(target.connectUrl)}/recordingOptions`
),
}).subscribe({
next: (formOptions) => {
next: ({ templates, recordingOptions }) => {
setErrorMessage('');
setTemplates(formOptions.templates);
setRecordingOptions(formOptions.recordingOptions);
setTemplates(templates);
setTemplate((old) => {
const matched = templates.find((t) => t.name === old.name && t.type === t.type);
return matched ? { name: matched.name, type: matched.type } : {};
});
setRecordingOptions(recordingOptions);
},
error: (error) => {
setErrorMessage(isTargetAgentHttp(target) ? 'Unsupported operation: Create Recordings' : error.message); // If both throw, first error will be shown
setTemplates([]);
setTemplate({});
setRecordingOptions({});
},
})
);
},
[addSubscription, context.api, setTemplates, setRecordingOptions, setErrorMessage]
[addSubscription, context.api, setTemplates, setTemplate, setRecordingOptions, setErrorMessage]
);

React.useEffect(() => {
addSubscription(
context.target.authFailure().subscribe(() => {
setErrorMessage(authFailMessage);
setTemplates([]);
setTemplate({});
setRecordingOptions({});
})
);
}, [context.target, setErrorMessage, addSubscription, setTemplates, setRecordingOptions]);
}, [context.target, setErrorMessage, addSubscription, setTemplates, setTemplate, setRecordingOptions]);

React.useEffect(() => {
addSubscription(context.target.target().subscribe(refreshFormOptions));
Expand All @@ -349,11 +357,11 @@ export const CustomRecordingForm: React.FC<CustomRecordingFormProps> = ({ prefil
return (
nameValid !== ValidatedOptions.success ||
durationValid !== ValidatedOptions.success ||
!templateName ||
!templateType ||
!template.name ||
!template.type ||
labelsValid !== ValidatedOptions.success
);
}, [nameValid, durationValid, templateName, templateType, labelsValid]);
}, [nameValid, durationValid, template, labelsValid]);

const hasReservedLabels = React.useMemo(
() => labels.some((label) => label.key === 'template.name' || label.key === 'template.type'),
Expand All @@ -371,11 +379,12 @@ export const CustomRecordingForm: React.FC<CustomRecordingFormProps> = ({ prefil
);

const selectedSpecifier = React.useMemo(() => {
if (templateName && templateType) {
return `${templateName},${templateType}`;
const { name, type } = template;
if (name && type) {
return `${name},${type}`;
}
return '';
}, [templateName, templateType]);
}, [template]);

const authRetry = React.useCallback(() => {
context.target.setAuthRetry();
Expand Down Expand Up @@ -477,14 +486,14 @@ export const CustomRecordingForm: React.FC<CustomRecordingFormProps> = ({ prefil
label="Template"
isRequired
fieldId="recording-template"
validated={!templateName ? ValidatedOptions.default : ValidatedOptions.success}
validated={!template.name ? ValidatedOptions.default : ValidatedOptions.success}
helperText={'The Event Template to be applied in this recording'}
helperTextInvalid="A Template must be selected"
>
<SelectTemplateSelectorForm
selected={selectedSpecifier}
templates={templates}
validated={!templateName ? ValidatedOptions.default : ValidatedOptions.success}
validated={!template.name ? ValidatedOptions.default : ValidatedOptions.success}
disabled={loading}
onSelect={handleTemplateChange}
/>
Expand Down
67 changes: 42 additions & 25 deletions src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisConfigForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@
import { EventTemplate } from '@app/CreateRecording/CreateRecording';
import { authFailMessage, ErrorView, isAuthFail } from '@app/ErrorView/ErrorView';
import { LoadingView } from '@app/LoadingView/LoadingView';
import { SelectTemplateSelectorForm } from '@app/Shared/SelectTemplateSelectorForm';
import {
AutomatedAnalysisRecordingConfig,
automatedAnalysisRecordingName,
TemplateType,
} from '@app/Shared/Services/Api.service';
import { ServiceContext } from '@app/Shared/Services/Services';
import { NO_TARGET, Target } from '@app/Shared/Services/Target.service';
import { SelectTemplateSelectorForm } from '@app/TemplateSelector/SelectTemplateSelectorForm';
import { useSubscriptions } from '@app/utils/useSubscriptions';
import {
Form,
Expand All @@ -64,7 +64,7 @@ import {
} from '@patternfly/react-core';
import * as React from 'react';
import { useTranslation } from 'react-i18next';
import { concatMap, filter, first, Observable } from 'rxjs';
import { concatMap, filter, first, Observable, take } from 'rxjs';

interface AutomatedAnalysisConfigFormProps {
useTitle?: boolean;
Expand All @@ -79,25 +79,29 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
const addSubscription = useSubscriptions();
const { t } = useTranslation();

const parseEventString = React.useMemo((): [string | undefined, TemplateType | undefined] => {
const parseEventString = React.useMemo((): Pick<Partial<EventTemplate>, 'name' | 'type'> => {
const eventString = context.settings.automatedAnalysisRecordingConfig().template;
if (!eventString) {
return [undefined, undefined];
return {};
}
const templateName = eventString.split(',')[0].split('=')[1];
const templateType = eventString.split(',')[1].split('=')[1];
if (!(templateType === 'TARGET' || templateType === 'CUSTOM')) {
console.error(`Invalid template type ${templateType}`);
return [undefined, undefined];
return {};
}
return [templateName, templateType];
return {
name: templateName,
type: templateType,
};
}, [context.settings]);

const [recordingConfig, setRecordingConfig] = React.useState<AutomatedAnalysisRecordingConfig>(
context.settings.automatedAnalysisRecordingConfig()
);
const [templates, setTemplates] = React.useState([] as EventTemplate[]);
const [templateName, setTemplateName] = React.useState<string | undefined>(parseEventString[0]);
const [templateType, setTemplateType] = React.useState<TemplateType | undefined>(parseEventString[1]);

const [templates, setTemplates] = React.useState<EventTemplate[]>([]);
const [template, setTemplate] = React.useState<Pick<Partial<EventTemplate>, 'name' | 'type'>>(parseEventString);
const [maxAge, setMaxAge] = React.useState(recordingConfig.maxAge);
const [maxAgeUnits, setMaxAgeUnits] = React.useState(1);
const [maxSize, setMaxSize] = React.useState(recordingConfig.maxSize);
Expand All @@ -111,7 +115,7 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
(targetObs ? targetObs : context.target.target())
.pipe(
filter((target) => target !== NO_TARGET),
first(),
take(1), // first() will throw EmptyError
concatMap((target) =>
context.api
.doGet<EventTemplate[]>(`targets/${encodeURIComponent(target.connectUrl)}/templates`)
Expand All @@ -122,14 +126,28 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
next: (templates) => {
setErrorMessage('');
setTemplates(templates);
setTemplate((old) => {
const matched = templates.find((t) => t.name === old.name && t.type === t.type);
return matched ? { name: matched.name, type: matched.type } : {};
});
},
error: (err) => {
setErrorMessage(err.message);
setTemplates([]);
setTemplate({});
},
})
);
}, [addSubscription, context.target, context.api, setErrorMessage, setTemplates, setIsLoading, targetObs]);
}, [
addSubscription,
context.target,
context.api,
setErrorMessage,
setTemplates,
setTemplate,
setIsLoading,
targetObs,
]);

React.useEffect(() => {
addSubscription(
Expand Down Expand Up @@ -203,29 +221,29 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr

const handleTemplateChange = React.useCallback(
(templateName?: string, templateType?: TemplateType) => {
setTemplateName(templateName);
setTemplateType(templateType);
if (!templateName || !templateType) {
return;
}
setTemplate({
name: templateName,
type: templateType,
});
setAAConfig({
...recordingConfig,
template: getEventString(templateName || '', templateType || ''),
});
},
[recordingConfig, setTemplateName, setTemplateType, setAAConfig, getEventString]
[setTemplate, recordingConfig, getEventString, setAAConfig]
);

const authRetry = React.useCallback(() => {
context.target.setAuthRetry();
}, [context.target]);

const selectedSpecifier = React.useMemo(() => {
if (templateName && templateType) {
return `${templateName},${templateType}`;
const { name, type } = template;
if (name && type) {
return `${name},${type}`;
}
return '';
}, [templateName, templateType]);
}, [template]);

const formContent = React.useMemo(
() => (
Expand All @@ -234,13 +252,13 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
label={t(`TEMPLATE`, { ns: 'common' })}
isRequired
fieldId="recording-template"
validated={!templateName ? ValidatedOptions.error : ValidatedOptions.success}
validated={!template.name ? ValidatedOptions.error : ValidatedOptions.success}
helperText={t('AutomatedAnalysisConfigForm.TEMPLATE_HELPER_TEXT')}
helperTextInvalid={t('TEMPLATE_HELPER_TEXT_INVALID', { ns: 'common' })}
>
<SelectTemplateSelectorForm
templates={templates}
validated={!templateName ? ValidatedOptions.error : ValidatedOptions.success}
validated={!template.name ? ValidatedOptions.error : ValidatedOptions.success}
onSelect={handleTemplateChange}
selected={selectedSpecifier}
/>
Expand Down Expand Up @@ -305,7 +323,7 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
</SplitItem>
</Split>
</FormGroup>
{templateType == 'TARGET' && (
{template.type == 'TARGET' && (
<HelperText className={`${automatedAnalysisRecordingName}-config-save-template-warning-helper`}>
<HelperTextItem variant="warning">
<Text component={TextVariants.p}>{t('AutomatedAnalysisConfigForm.TEMPLATE_INVALID_WARNING')}</Text>
Expand All @@ -316,8 +334,7 @@ export const AutomatedAnalysisConfigForm: React.FC<AutomatedAnalysisConfigFormPr
),
[
t,
templateName,
templateType,
template,
templates,
selectedSpecifier,
maxSize,
Expand Down
Loading

0 comments on commit 7077cf1

Please sign in to comment.