Skip to content

Commit

Permalink
Add a numerical input to Create Periodic Scheduled Runs
Browse files Browse the repository at this point in the history
  • Loading branch information
dpanshug committed Sep 12, 2023
1 parent 6ee8280 commit eda08a4
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 15 deletions.
61 changes: 61 additions & 0 deletions frontend/src/__tests__/unit/utils/string.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { replaceNonNumericPartWithString, replaceNumericPartWithString } from '~/utilities/string';

describe('replaceNumericPartWithString', () => {
it('should replace the numeric part of a string with a number', () => {
expect(replaceNumericPartWithString('abc123xyz', 456)).toBe('abc456xyz');
});

it('should handle empty input string', () => {
expect(replaceNumericPartWithString('', 789)).toBe('789');
});

it('should handle input string without numeric part', () => {
expect(replaceNumericPartWithString('abcdef', 123)).toBe('123abcdef');
});

it('should handle numeric part at the beginning of the string', () => {
expect(replaceNumericPartWithString('123xyz', 789)).toBe('789xyz');
});

it('should handle numeric part at the end of the string', () => {
expect(replaceNumericPartWithString('abc456', 123)).toBe('abc123');
});

it('should handle zero numeric replacement', () => {
expect(replaceNumericPartWithString('abc0xyz', 123)).toBe('abc123xyz');
});
});

describe('replaceNonNumericPartWithString', () => {
it('should replace the non-numeric part of a string with another string', () => {
expect(replaceNonNumericPartWithString('abc123xyz', 'XYZ')).toBe('XYZ123xyz');
});

it('should handle empty input string', () => {
expect(replaceNonNumericPartWithString('', 'XYZ')).toBe('XYZ');
});

it('should handle input string with no non-numeric part', () => {
expect(replaceNonNumericPartWithString('123', 'XYZ')).toBe('123XYZ');
});

it('should handle input string with only non-numeric part', () => {
expect(replaceNonNumericPartWithString('abc', 'XYZ')).toBe('XYZ');
});

it('should handle input string with multiple non-numeric parts', () => {
expect(replaceNonNumericPartWithString('abc123def456', 'XYZ')).toBe('XYZ123def456');
});

it('should handle replacement string containing numbers', () => {
expect(replaceNonNumericPartWithString('abc123xyz', '123')).toBe('123123xyz');
});

it('should handle replacement string containing special characters', () => {
expect(replaceNonNumericPartWithString('abc123xyz', '@#$')).toBe('@#$123xyz');
});

it('should handle replacement string containing spaces', () => {
expect(replaceNonNumericPartWithString('abc123xyz', ' ')).toBe(' 123xyz');
});
});
27 changes: 27 additions & 0 deletions frontend/src/__tests__/unit/utils/time.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { convertPeriodicTimeToSeconds } from '~/utilities/time';

describe('Convert periodic time to seconds', () => {
it('should convert hours to seconds', () => {
expect(convertPeriodicTimeToSeconds('5Hour')).toBe(5 * 3600);
});

it('should convert minutes to seconds', () => {
expect(convertPeriodicTimeToSeconds('6Minute')).toBe(6 * 60);
});

it('should convert days to seconds', () => {
expect(convertPeriodicTimeToSeconds('221Day')).toBe(221 * 86400);
});

it('should convert weeks to seconds', () => {
expect(convertPeriodicTimeToSeconds('12Week')).toBe(12 * 7 * 24 * 60 * 60);
});

it('should default to 0 seconds for unrecognized units', () => {
expect(convertPeriodicTimeToSeconds('3Weeks')).toBe(0);
});

it('should default to 0 seconds for NaN', () => {
expect(convertPeriodicTimeToSeconds('hour12')).toBe(0);
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import * as React from 'react';
import { ClipboardCopy, Radio, Stack, StackItem, Text } from '@patternfly/react-core';
import {
ClipboardCopy,
InputGroup,
Radio,
Stack,
StackItem,
Text,
TextInput,
} from '@patternfly/react-core';
import {
PeriodicOptions,
RunTypeScheduledData,
Expand All @@ -12,6 +20,7 @@ import {
DEFAULT_PERIODIC_OPTION,
} from '~/concepts/pipelines/content/createRun/const';
import EndDateBeforeStartDateError from '~/concepts/pipelines/content/createRun/contentSections/EndDateBeforeStartDateError';
import { replaceNonNumericPartWithString, replaceNumericPartWithString } from '~/utilities/string';

type RunTypeSectionScheduledProps = {
data: RunTypeScheduledData;
Expand All @@ -32,22 +41,45 @@ const RunTypeSectionScheduled: React.FC<RunTypeSectionScheduledProps> = ({ data,
isChecked={data.triggerType === ScheduledType.PERIODIC}
id={ScheduledType.PERIODIC}
onChange={() =>
onChange({ ...data, triggerType: ScheduledType.PERIODIC, value: DEFAULT_PERIODIC_OPTION })
onChange({
...data,
triggerType: ScheduledType.PERIODIC,
value: DEFAULT_PERIODIC_OPTION,
})
}
body={
data.triggerType === ScheduledType.PERIODIC && (
<>
<Text>
<b>Run every</b>
</Text>
<SimpleDropdownSelect
options={Object.values(PeriodicOptions).map((v) => ({
key: v,
label: v,
}))}
value={data.value}
onChange={(value) => onChange({ ...data, value })}
/>
<InputGroup>
<TextInput
type="number"
id="input-schedule-time"
aria-label="input periodic schedule time"
value={parseInt(data.value) || 0}
onChange={(value) =>
onChange({
...data,
value: replaceNumericPartWithString(data.value, parseInt(value)),
})
}
/>
<SimpleDropdownSelect
options={Object.values(PeriodicOptions).map((v) => ({
key: v,
label: v,
}))}
value={data.value.replace(/[0-9]/g, '')}
onChange={(value) =>
onChange({
...data,
value: replaceNonNumericPartWithString(data.value, value),
})
}
/>
</InputGroup>
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {
periodicOptionAsSeconds,
RunDateTime,
RunFormData,
RunTypeOption,
Expand All @@ -16,6 +15,7 @@ import {
} from '~/concepts/pipelines/kfTypes';
import { PipelineAPIs } from '~/concepts/pipelines/types';
import { isFilledRunFormData } from '~/concepts/pipelines/content/createRun/utils';
import { convertPeriodicTimeToSeconds } from '~/utilities/time';

const getResourceReferences = (formData: SafeRunFormData): ResourceReferenceKF[] => {
const refs: ResourceReferenceKF[] = [];
Expand Down Expand Up @@ -79,6 +79,7 @@ const createJob = async (

const startDate = convertDateDataToKFDateTime(formData.runType.data.start) ?? undefined;
const endDate = convertDateDataToKFDateTime(formData.runType.data.end) ?? undefined;
const periodicScheduleIntervalTime = convertPeriodicTimeToSeconds(formData.runType.data.value);
/* eslint-disable camelcase */
const data: CreatePipelineRunJobKFData = {
name: formData.nameDesc.name,
Expand All @@ -93,10 +94,7 @@ const createJob = async (
periodic_schedule:
formData.runType.data.triggerType === ScheduledType.PERIODIC
? {
interval_second:
periodicOptionAsSeconds[
formData.runType.data.value as keyof typeof periodicOptionAsSeconds
].toString(),
interval_second: periodicScheduleIntervalTime.toString(),
start_time: startDate,
end_time: endDate,
}
Expand Down
55 changes: 55 additions & 0 deletions frontend/src/utilities/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,58 @@ export const genRandomChars = (len = 6): string =>
.toString(36)
.replace(/[^a-z0-9]+/g, '')
.substr(1, len);

/**
* This function replaces the first occurrence of a numeric part in the input string
* with the specified replacement numeric value.
* @param inputString
* @param replacementString
*/
export const replaceNumericPartWithString = (
inputString: string,
replacementString: number,
): string => {
// If the input string is empty or contains only whitespace, return the replacement as a string.
if (inputString.trim() === '') {
return replacementString.toString();
}

const match = inputString.match(/\d+/); //Find numeric part in string (only first occurance)
let updatedString = inputString;

if (match) {
const matchedNumber = match[0];
updatedString = inputString.replace(matchedNumber, String(replacementString));
} else {
// If no numeric part is found, prepend the replacement numeric value to the input string.
updatedString = replacementString + inputString;
}
return updatedString;
};

/**
* This function replaces the first occurrence of a non-numeric part in the input string
* with the specified replacement string.
* @param inputString
* @param replacementString
*/
export const replaceNonNumericPartWithString = (
inputString: string,
replacementString: string,
): string => {
if (inputString.trim() === '') {
return replacementString;
}

const match = inputString.match(/\D+/); //Find non-numeric part in string (only first occurance)
let updatedString = inputString;

if (match) {
const matchedString = match[0];
updatedString = inputString.replace(matchedString, replacementString);
} else {
// If no non-numeric part is found, append the replacement non-numeric value to the input string.
updatedString = inputString + replacementString;
}
return updatedString;
};
24 changes: 24 additions & 0 deletions frontend/src/utilities/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,27 @@ export const relativeTime = (current: number, previous: number): string => {

return `${date.getDate()} ${monthAsString} ${date.getFullYear()}`;
};

/** Function to convert time strings like "2Hour" to seconds */
export const convertPeriodicTimeToSeconds = (timeString: string): number => {
const numericValue = parseInt(timeString, 10);

if (isNaN(numericValue)) {
return 0; // return 0 if unit is not recognized
}

const unit = timeString.toLowerCase().replace(/\d+/g, '');

switch (unit) {
case 'hour':
return numericValue * 60 * 60; // 1 hour = 3600 seconds
case 'minute':
return numericValue * 60; // 1 minute = 60 seconds
case 'day':
return numericValue * 24 * 60 * 60; // 1 day = 86400 seconds
case 'week':
return numericValue * 7 * 24 * 60 * 60; // 1 week = 604800 seconds
default:
return 0; // Default to 0 if unit is not recognized
}
};

0 comments on commit eda08a4

Please sign in to comment.