Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion webpack/JobInvocationDetail/JobInvocationActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
UPDATE_JOB,
} from './JobInvocationConstants';

const POLLING_INTERVAL_MS = 1000;

export const getJobInvocation = url => dispatch => {
const fetchData = withInterval(
get({
Expand All @@ -32,7 +34,7 @@ export const getJobInvocation = url => dispatch => {
response?.data?.error?.message ||
'Error',
}),
1000
POLLING_INTERVAL_MS
);

dispatch(fetchData);
Expand Down
1 change: 1 addition & 0 deletions webpack/JobInvocationDetail/JobInvocationConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const GET_TEMPLATE_INVOCATIONS = 'GET_TEMPLATE_INVOCATIONS';
export const CHANGE_ENABLED_RECURRING_LOGIC = 'CHANGE_ENABLED_RECURRING_LOGIC';
export const CANCEL_RECURRING_LOGIC = 'CANCEL_RECURRING_LOGIC';
export const GET_REPORT_TEMPLATES = 'GET_REPORT_TEMPLATES';
export const GET_REPORT_TEMPLATE_SETTING = 'GET_REPORT_TEMPLATE_SETTING';
export const GET_REPORT_TEMPLATE_INPUTS = 'GET_REPORT_TEMPLATE_INPUTS';
export const JOB_INVOCATION_HOSTS = 'JOB_INVOCATION_HOSTS';
export const GET_TEMPLATE_INVOCATION = 'GET_TEMPLATE_INVOCATION';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ const JobInvocationSystemStatusChart = ({
return '0';
};
const chartSize = 105;
const [legendWidth, setLegendWidth] = useState(270);
const defaultLegendWidth = 270;
const [legendWidth, setLegendWidth] = useState(defaultLegendWidth);

// Calculates chart legend width based on its content
useEffect(() => {
Expand Down
40 changes: 28 additions & 12 deletions webpack/JobInvocationDetail/JobInvocationToolbarButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import {
STATUS,
GET_REPORT_TEMPLATES,
GET_REPORT_TEMPLATE_SETTING,
GET_REPORT_TEMPLATE_INPUTS,
} from './JobInvocationConstants';
import { selectTaskCancelable } from './JobInvocationSelectors';
Expand Down Expand Up @@ -80,22 +81,37 @@ const JobInvocationToolbarButtons = ({ jobId, data }) => {

useEffect(() => {
let isMounted = true;
const fetchReportTemplate = templateName => {
dispatch(
get({
key: GET_REPORT_TEMPLATES,
url: '/api/report_templates',
params: {
search: `name="${templateName}"`,
per_page: 1,
},
handleSuccess: ({ data: { results } }) => {
if (isMounted) {
setReportTemplateJobId(results[0]?.id);
}
},
handleError: () => {
if (isMounted) {
setReportTemplateJobId(undefined);
}
},
})
);
};
dispatch(
get({
key: GET_REPORT_TEMPLATES,
url: '/api/report_templates',
handleSuccess: ({ data: { results } }) => {
if (isMounted) {
setReportTemplateJobId(
results.find(result => result.name === 'Job - Invocation Report')
?.id
);
}
key: GET_REPORT_TEMPLATE_SETTING,
url: '/api/settings/remote_execution_job_invocation_report_template',
handleSuccess: ({ data: { value } }) => {
fetchReportTemplate(value);
},
handleError: () => {
if (isMounted) {
setReportTemplateJobId(undefined);
}
fetchReportTemplate('Job - Invocation Report');
},
})
);
Expand Down
8 changes: 6 additions & 2 deletions webpack/JobInvocationDetail/TemplateInvocation.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ import { OutputToggleGroup } from './TemplateInvocationComponents/OutputToggleGr
import { PreviewTemplate } from './TemplateInvocationComponents/PreviewTemplate';
import { OutputCodeBlock } from './TemplateInvocationComponents/OutputCodeBlock';

const COPIED_EXIT_DELAY_MS = 1500;
const DEFAULT_EXIT_DELAY_MS = 600;
const AUTO_REFRESH_INTERVAL_MS = 5000;

const CopyToClipboard = ({ fullOutput }) => {
const clipboardCopyFunc = async (event, text) => {
try {
Expand All @@ -41,7 +45,7 @@ const CopyToClipboard = ({ fullOutput }) => {
textId="code-content"
aria-label="Copy to clipboard"
onClick={e => onClick(e, fullOutput)}
exitDelay={copied ? 1500 : 600}
exitDelay={copied ? COPIED_EXIT_DELAY_MS : DEFAULT_EXIT_DELAY_MS}
maxWidth="110px"
variant="plain"
onTooltipHidden={() => setCopied(false)}
Expand Down Expand Up @@ -111,7 +115,7 @@ export const TemplateInvocation = ({
} else if (intervalRef.current) {
clearInterval(intervalRef.current);
}
}, 5000);
}, AUTO_REFRESH_INTERVAL_MS);
}

return () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import PropTypes from 'prop-types';
import { Button } from '@patternfly/react-core';
import { translate as __ } from 'foremanReact/common/I18n';

const MS_PER_SECOND = 1000;

export const OutputCodeBlock = ({ code, showOutputType, scrollElement }) => {
let lineCounter = 0;
// eslint-disable-next-line no-control-regex
Expand Down Expand Up @@ -122,7 +124,7 @@ export const OutputCodeBlock = ({ code, showOutputType, scrollElement }) => {
<div key={index} className={`line ${line.output_type}`}>
<span
className="counter"
title={new Date(line.timestamp * 1000).toISOString()}
title={new Date(line.timestamp * MS_PER_SECOND).toISOString()}
>
{lineCounter.toString().padStart(4, '\u00A0')}:{' '}
</span>
Expand Down
68 changes: 67 additions & 1 deletion webpack/JobInvocationDetail/__tests__/MainInformation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
CANCEL_RECURRING_LOGIC,
CHANGE_ENABLED_RECURRING_LOGIC,
GET_REPORT_TEMPLATES,
GET_REPORT_TEMPLATE_SETTING,
GET_REPORT_TEMPLATE_INPUTS,
GET_TASK,
JOB_INVOCATION_KEY,
Expand Down Expand Up @@ -85,7 +86,13 @@ jest.mock('foremanReact/routes/common/PageLayout/PageLayout', () =>

const setupApiMocks = () => {
api.get.mockImplementation(({ handleSuccess, key, ...action }) => {
if (key === GET_REPORT_TEMPLATES) {
if (key === GET_REPORT_TEMPLATE_SETTING) {
if (handleSuccess) {
handleSuccess({
data: { value: 'Job - Invocation Report' },
});
}
} else if (key === GET_REPORT_TEMPLATES) {
if (handleSuccess) {
handleSuccess({
data: mockReportTemplatesResponse,
Expand Down Expand Up @@ -323,6 +330,59 @@ describe('JobInvocationDetailPage', () => {
expect(createReportButton).toHaveClass('pf-m-disabled');
});

it('falls back to default template name when settings API fails', async () => {
api.get.mockImplementation(
({ handleSuccess, handleError, key, ...action }) => {
if (key === GET_REPORT_TEMPLATE_SETTING) {
if (handleError) {
handleError({ message: 'Not found' });
}
} else if (key === GET_REPORT_TEMPLATES) {
if (handleSuccess) {
handleSuccess({
data: mockReportTemplatesResponse,
});
}
} else if (key === GET_REPORT_TEMPLATE_INPUTS) {
if (handleSuccess) {
handleSuccess({
data: mockReportTemplateInputsResponse,
});
}
}

return { type: 'get', key, ...action };
}
);

const jobId = jobInvocationData.id;

renderJobInvocationDetailPage(createJobInvocationDetailState(), {
jobId,
});

expect(api.get).toHaveBeenCalledWith(
expect.objectContaining({
key: GET_REPORT_TEMPLATE_SETTING,
url: '/api/settings/remote_execution_job_invocation_report_template',
})
);
expect(api.get).toHaveBeenCalledWith(
expect.objectContaining({
key: GET_REPORT_TEMPLATES,
url: '/api/report_templates',
params: expect.objectContaining({
search: 'name="Job - Invocation Report"',
}),
})
);
expect(screen.getByText('Create report').getAttribute('href')).toEqual(
foremanUrl(
`/templates/report_templates/${mockReportTemplatesResponse.results[0].id}/generate?report_template_report%5Binput_values%5D%5B${mockReportTemplateInputsResponse.results[0].id}%5D%5Bvalue%5D=${jobId}`
)
);
});

it('should dispatch global actions', async () => {
const actualActions = jest.requireActual('../JobInvocationActions');
const { getTask } = jest.requireMock('../JobInvocationActions');
Expand All @@ -342,6 +402,12 @@ describe('JobInvocationDetailPage', () => {
{ jobId }
);

expect(api.get).toHaveBeenCalledWith(
expect.objectContaining({
key: GET_REPORT_TEMPLATE_SETTING,
url: '/api/settings/remote_execution_job_invocation_report_template',
})
);
expect(api.get).toHaveBeenCalledWith(
expect.objectContaining({
key: GET_REPORT_TEMPLATES,
Expand Down
4 changes: 3 additions & 1 deletion webpack/JobWizard/JobWizard.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import { StartsBeforeErrorAlert, StartsAtErrorAlert } from './StartsErrorAlert';
import { Footer } from './Footer';
import './JobWizard.scss';

const SCHEDULE_CHECK_INTERVAL_MS = 5000;

export const JobWizard = ({ rerunData }) => {
const routerSearch = useSelector(selectRouterSearch);
const [feature, setFeature] = useState(routerSearch.feature);
Expand Down Expand Up @@ -237,7 +239,7 @@ export const JobWizard = ({ rerunData }) => {
}
};
updateStartsError();
const interval = setInterval(updateStartsError, 5000);
const interval = setInterval(updateStartsError, SCHEDULE_CHECK_INTERVAL_MS);

return () => {
interval && clearInterval(interval);
Expand Down
8 changes: 6 additions & 2 deletions webpack/JobWizard/steps/Schedule/RepeatHour.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ import {
import { translate as __ } from 'foremanReact/common/I18n';
import { helpLabel } from '../form/FormHelpers';

const MINUTES_IN_HOUR = 60;
// eslint-disable-next-line no-magic-numbers
const DEFAULT_MINUTE_OPTIONS = [0, 15, 30, 45];

export const RepeatHour = ({ repeatData, setRepeatData }) => {
const isValidMinute = newMinute =>
Number.isInteger(parseInt(newMinute, 10)) &&
newMinute >= 0 &&
newMinute < 60;
newMinute < MINUTES_IN_HOUR;

const { minute } = repeatData;
useEffect(() => {
Expand All @@ -27,7 +31,7 @@ export const RepeatHour = ({ repeatData, setRepeatData }) => {
}
}, [minute, setRepeatData]);
const [minuteOpen, setMinuteOpen] = useState(false);
const [options, setOptions] = useState([0, 15, 30, 45]);
const [options, setOptions] = useState(DEFAULT_MINUTE_OPTIONS);
const [isAlertOpen, setIsAlertOpen] = useState(false);
return (
<FormGroup
Expand Down
7 changes: 5 additions & 2 deletions webpack/JobWizard/steps/Schedule/RepeatWeek.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import { translate as __, documentLocale } from 'foremanReact/common/I18n';
import { RepeatDaily } from './RepeatDaily';
import { noop } from '../../../helpers';

const SUNDAY_REFERENCE_YEAR = 2017;
const DAYS_IN_WEEK = 7;

export const getWeekDays = () => {
const locale = documentLocale().replace(/-/g, '_');
const baseDate = new Date(Date.UTC(2017, 0, 1)); // just a Sunday
const baseDate = new Date(Date.UTC(SUNDAY_REFERENCE_YEAR, 0, 1));
const weekDays = [];
const formatOptions = { weekday: 'short', timeZone: 'UTC' };
for (let i = 0; i < 7; i++) {
for (let i = 0; i < DAYS_IN_WEEK; i++) {
try {
weekDays.push(baseDate.toLocaleDateString(locale, formatOptions));
} catch {
Expand Down
6 changes: 4 additions & 2 deletions webpack/JobWizard/steps/Schedule/ScheduleRecurring.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { PurposeField } from './PurposeField';
import { DateTimePicker } from '../form/DateTimePicker';
import { WizardTitle } from '../form/WizardTitle';

const ONE_MINUTE_MS = 60000;

export const ScheduleRecurring = ({
scheduleValue,
setScheduleValue,
Expand Down Expand Up @@ -114,8 +116,8 @@ export const ScheduleRecurring = ({
setScheduleValue(current => ({
...current,
startsAt: new Date(
new Date().getTime() + 60000
).toISOString(), // 1 minute in the future
new Date().getTime() + ONE_MINUTE_MS
).toISOString(),
isFuture: true,
}))
}
Expand Down
6 changes: 5 additions & 1 deletion webpack/JobWizard/steps/Schedule/ScheduleType.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
import { WizardTitle } from '../form/WizardTitle';
import { QueryType } from './QueryType';

const ONE_MINUTE_MS = 60000;

export const ScheduleType = ({
scheduleType,
isTypeStatic,
Expand Down Expand Up @@ -49,7 +51,9 @@ export const ScheduleType = ({
onChange={() => {
setScheduleValue(current => ({
...current,
startsAt: new Date(new Date().getTime() + 60000).toISOString(), // 1 minute in the future
startsAt: new Date(
new Date().getTime() + ONE_MINUTE_MS
).toISOString(),
scheduleType: SCHEDULE_TYPES.FUTURE,
repeatType: repeatTypes.noRepeat,
}));
Expand Down
17 changes: 11 additions & 6 deletions webpack/JobWizard/steps/form/DateTimePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,17 @@ import {
import { debounce } from 'lodash';
import { translate as __, documentLocale } from 'foremanReact/common/I18n';

const PAD_SLICE = -2;
const DEBOUNCE_MS = 1000;

const formatDateTime = d =>
`${d.getFullYear()}-${`0${d.getMonth() + 1}`.slice(
-2
)}-${`0${d.getDate()}`.slice(-2)} ${`0${d.getHours()}`.slice(
-2
)}:${`0${d.getMinutes()}`.slice(-2)}:${`0${d.getSeconds()}`.slice(-2)}`;
PAD_SLICE
)}-${`0${d.getDate()}`.slice(PAD_SLICE)} ${`0${d.getHours()}`.slice(
PAD_SLICE
)}:${`0${d.getMinutes()}`.slice(PAD_SLICE)}:${`0${d.getSeconds()}`.slice(
PAD_SLICE
)}`;

export const DateTimePicker = ({
dateTime,
Expand Down Expand Up @@ -103,7 +108,7 @@ export const DateTimePicker = ({
aria-label={`${ariaLabel} datepicker`}
value={formattedDate}
placeholder="yyyy/mm/dd"
onChange={debounce(onDateChange, 1000, {
onChange={debounce(onDateChange, DEBOUNCE_MS, {
leading: false,
trailing: true,
})}
Expand All @@ -123,7 +128,7 @@ export const DateTimePicker = ({
time={dateTime ? dateObject.toString() : ''}
inputProps={dateTime ? {} : { value: '' }}
placeholder={includeSeconds ? 'hh:mm:ss' : 'hh:mm'}
onChange={debounce(onTimeChange, 1000, {
onChange={debounce(onTimeChange, DEBOUNCE_MS, {
leading: false,
trailing: true,
})}
Expand Down
4 changes: 3 additions & 1 deletion webpack/react_app/components/TargetingHosts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { TARGETING_HOSTS } from './TargetingHostsConsts';
import TargetingHostsPage from './TargetingHostsPage';
import { chartFilter } from '../../redux/actions/jobInvocations';

const POLLING_INTERVAL_MS = 1000;

const buildSearchQuery = (query, stateFilter) => {
const filters = Object.entries(stateFilter).map(
([key, value]) => `${key} = ${value}`
Expand Down Expand Up @@ -100,7 +102,7 @@ const WrappedTargetingHosts = () => {
dispatch(stopInterval(TARGETING_HOSTS));
},
}),
1000
POLLING_INTERVAL_MS
),
[dispatch]
);
Expand Down
Loading
Loading