From 5471f7dea53029866d543991071105dc1c024b51 Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Mon, 19 Oct 2020 21:58:50 +0200 Subject: [PATCH 1/4] feat: render CronSchedule Render CronSchedule --- package.json | 2 +- src/common/formatters.ts | 13 +++++++ src/common/test/formatters.spec.ts | 38 +++++++++++++++++++++ src/components/Entities/EntitySchedules.tsx | 11 ++++-- src/components/Launch/SchedulesTable.tsx | 17 ++++++++- src/components/Launch/constants.ts | 1 + yarn.lock | 35 +++++-------------- 7 files changed, 87 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 696563810..163bc7230 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "@commitlint/cli": "^8.3.5", "@commitlint/config-conventional": "^8.3.4", "@date-io/moment": "1.3.9", - "@lyft/flyteidl": "^0.18.4", + "@lyft/flyteidl": "^0.18.9", "@material-ui/core": "^4.0.0", "@material-ui/icons": "^4.0.0", "@material-ui/pickers": "^3.2.2", diff --git a/src/common/formatters.ts b/src/common/formatters.ts index 5d29f77e7..f666b4b21 100644 --- a/src/common/formatters.ts +++ b/src/common/formatters.ts @@ -134,6 +134,19 @@ export function getScheduleFrequencyString(schedule?: Admin.ISchedule) { if (schedule.rate) { return fixedRateToString(schedule.rate); } + if (schedule.cronSchedule && schedule.cronSchedule.schedule) { + return cronstrue.toString(`${schedule.cronSchedule.schedule}`); + } + return ''; +} + +export function getScheduleOffsetString(schedule?: Admin.ISchedule) { + if (schedule == null) { + return ''; + } + if (schedule.cronSchedule) { + return schedule.cronSchedule.offset; + } return ''; } diff --git a/src/common/test/formatters.spec.ts b/src/common/test/formatters.spec.ts index 5f209743a..d6c032c02 100644 --- a/src/common/test/formatters.spec.ts +++ b/src/common/test/formatters.spec.ts @@ -1,4 +1,5 @@ import { millisecondsToDuration } from 'common/utils'; +import { Admin } from 'flyteidl'; import { subSecondString, unknownValueString, @@ -12,6 +13,8 @@ import { formatDate, formatDateLocalTimezone, formatDateUTC, + getScheduleFrequencyString, + getScheduleOffsetString, leftPaddedNumber, millisecondsToHMS, protobufDurationToHMS @@ -160,6 +163,41 @@ describe('millisecondsToHMS', () => { ); }); +describe('getScheduleFrequencyString', () => { + // input and expected result + const cases: [Admin.ISchedule, string][] = [ + [{ cronExpression: '* * * * *' }, 'Every minute'], + [ + { rate: { value: 1, unit: Admin.FixedRateUnit.MINUTE } }, + 'Every 1 minutes' + ], + [{ cronSchedule: { schedule: '* * * * *' } }, 'Every minute'], + [null!, ''], + [{ cronSchedule: { schedule: '' } }, ''] + ]; + + cases.forEach(([input, expected]) => + it(`should produce ${expected} with input ${input}`, () => { + expect(getScheduleFrequencyString(input)).toEqual(expected); + }) + ); +}); + +describe('getScheduleOffsetString', () => { + // input and expected result + const cases: [Admin.ISchedule, string][] = [ + [{ cronSchedule: { offset: 'P1D' } }, 'P1D'], + [null!, ''], + [{ cronSchedule: { offset: '' } }, ''] + ]; + + cases.forEach(([input, expected]) => + it(`should produce ${expected} with input ${input}`, () => { + expect(getScheduleOffsetString(input)).toEqual(expected); + }) + ); +}); + describe('ensureUrlWithProtocol', () => { // input and expected result const cases: [string, string][] = [ diff --git a/src/components/Entities/EntitySchedules.tsx b/src/components/Entities/EntitySchedules.tsx index 3b04b4852..06e8e5037 100644 --- a/src/components/Entities/EntitySchedules.tsx +++ b/src/components/Entities/EntitySchedules.tsx @@ -1,6 +1,9 @@ import { Typography } from '@material-ui/core'; import { makeStyles, Theme } from '@material-ui/core/styles'; -import { getScheduleFrequencyString } from 'common/formatters'; +import { + getScheduleFrequencyString, + getScheduleOffsetString +} from 'common/formatters'; import { WaitForData } from 'components/common'; import { useCommonStyles } from 'components/common/styles'; import { useWorkflowSchedules } from 'components/hooks'; @@ -23,7 +26,11 @@ const RenderSchedules: React.FC<{ {launchPlans.map((launchPlan, idx) => { const { schedule } = launchPlan.spec.entityMetadata; const frequencyString = getScheduleFrequencyString(schedule); - return
  • {frequencyString}
  • ; + const offsetString = getScheduleOffsetString(schedule); + const scheduleString = offsetString + ? `${frequencyString} (offset: ${offsetString})` + : frequencyString; + return
  • {scheduleString}
  • ; })} ); diff --git a/src/components/Launch/SchedulesTable.tsx b/src/components/Launch/SchedulesTable.tsx index c3e220d5f..2a1a06284 100644 --- a/src/components/Launch/SchedulesTable.tsx +++ b/src/components/Launch/SchedulesTable.tsx @@ -1,7 +1,10 @@ import Chip from '@material-ui/core/Chip'; import { makeStyles, Theme } from '@material-ui/core/styles'; import * as classnames from 'classnames'; -import { getScheduleFrequencyString } from 'common/formatters'; +import { + getScheduleFrequencyString, + getScheduleOffsetString +} from 'common/formatters'; import { ListProps } from 'components/common'; import { useCommonStyles } from 'components/common/styles'; import { @@ -54,6 +57,18 @@ export const schedulesTableColumns: KeyedColumnProps[] = [ label: 'frequency', width: schedulesTableColumnsWidths.frequency }, + { + cellDataGetter: ({ rowData }: CellDataGetterParams) => + rowData.spec.entityMetadata.schedule, + cellRenderer: ({ cellData: schedule }: TableCellProps) => + getScheduleOffsetString(schedule), + dataKey: 'closure', + flexGrow: 1, + flexShrink: 0, + key: 'offset', + label: 'offset', + width: schedulesTableColumnsWidths.offset + }, { cellDataGetter: ({ rowData }: CellDataGetterParams) => isLaunchPlanActive(rowData), diff --git a/src/components/Launch/constants.ts b/src/components/Launch/constants.ts index f558c3668..44e624f39 100644 --- a/src/components/Launch/constants.ts +++ b/src/components/Launch/constants.ts @@ -7,5 +7,6 @@ export const launchPlansTableColumnWidths = { export const schedulesTableColumnsWidths = { active: 80, frequency: 300, + offset: 80, name: 250 }; diff --git a/yarn.lock b/yarn.lock index fda1a4c04..a76ae1bb8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1855,10 +1855,10 @@ dependencies: core-js "^2.5.7" -"@lyft/flyteidl@^0.18.4": - version "0.18.4" - resolved "https://registry.yarnpkg.com/@lyft/flyteidl/-/flyteidl-0.18.4.tgz#47177a33e082355a105f8276bfe15cd2388fcb4a" - integrity sha512-kQ40KN6ffUogPsQmaoLyVRvrg8tcxVaoVSDiGol4cnspeLxsB6728qBHzQY1oRcOeRBFCRASyImJrbbBVpNXeg== +"@lyft/flyteidl@^0.18.9": + version "0.18.9" + resolved "https://registry.yarnpkg.com/@lyft/flyteidl/-/flyteidl-0.18.9.tgz#e8c06a598329de052ca5b539c382749379a93ad7" + integrity sha512-BZCAiHpq+JMsd5u2m8UuedlL1E/CQZjDywXnOO27QmNzJv1zdcZ4QDkBU6ClCQQr/KOBszolyfRVY3N//3AG2Q== "@marionebl/sander@^0.6.0": version "0.6.1" @@ -7057,7 +7057,7 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.2.5: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -9720,7 +9720,7 @@ import-local@^3.0.2: pkg-dir "^4.2.0" resolve-cwd "^3.0.0" -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -11605,11 +11605,6 @@ lodash._basecopy@^3.0.0: resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36" integrity sha1-jaDmqHbPNEwK2KVIghEd08XHyjY= -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -11618,16 +11613,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*, lodash._bindcallback@^3.0.0: +lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" @@ -11637,19 +11627,12 @@ lodash._createassigner@^3.0.0: lodash._isiterateecall "^3.0.0" lodash.restparam "^3.0.0" -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: +lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= @@ -11770,7 +11753,7 @@ lodash.memoize@4.x, lodash.memoize@^4.1.2: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= -lodash.restparam@*, lodash.restparam@^3.0.0: +lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= From 027e57e84d2dcd2310cf469bd205ffce6429d3e4 Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Tue, 20 Oct 2020 21:25:17 +0200 Subject: [PATCH 2/4] feat: support schedule aliases --- src/common/formatters.ts | 31 +++++++++++++++++++++++++++++- src/common/test/formatters.spec.ts | 17 ++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/common/formatters.ts b/src/common/formatters.ts index f666b4b21..1bc9147b6 100644 --- a/src/common/formatters.ts +++ b/src/common/formatters.ts @@ -122,6 +122,31 @@ export function fixedRateToString({ value, unit }: Admin.IFixedRate): string { return `Every ${value} ${fixedRateUnitStrings[unit]}`; } +const hourlyAliases = ['@hourly', 'hourly', 'hours']; +const dailyAliases = ['@daily', 'daily', 'days']; +const weeklyAliases = ['@weekly', 'weekly', 'weeks']; +const monthlyAliases = ['@monthly', 'monthly', 'months']; +const yearlyAliases = ['@yearly', 'yearly', 'years', '@annually', 'annually']; + +export function getScheduleFrequencyStringFromAlias(schedule: string) { + if (hourlyAliases.includes(schedule)) { + return 'Every hour'; + } + if (dailyAliases.includes(schedule)) { + return 'Every day'; + } + if (weeklyAliases.includes(schedule)) { + return 'Every week'; + } + if (monthlyAliases.includes(schedule)) { + return 'Every month'; + } + if (yearlyAliases.includes(schedule)) { + return 'Every year'; + } + return ''; +} + export function getScheduleFrequencyString(schedule?: Admin.ISchedule) { if (schedule == null) { return ''; @@ -135,7 +160,11 @@ export function getScheduleFrequencyString(schedule?: Admin.ISchedule) { return fixedRateToString(schedule.rate); } if (schedule.cronSchedule && schedule.cronSchedule.schedule) { - return cronstrue.toString(`${schedule.cronSchedule.schedule}`); + return ( + getScheduleFrequencyStringFromAlias( + schedule.cronSchedule.schedule + ) || cronstrue.toString(schedule.cronSchedule.schedule) + ); } return ''; } diff --git a/src/common/test/formatters.spec.ts b/src/common/test/formatters.spec.ts index d6c032c02..0e3879e72 100644 --- a/src/common/test/formatters.spec.ts +++ b/src/common/test/formatters.spec.ts @@ -172,6 +172,23 @@ describe('getScheduleFrequencyString', () => { 'Every 1 minutes' ], [{ cronSchedule: { schedule: '* * * * *' } }, 'Every minute'], + [{ cronSchedule: { schedule: '@hourly' } }, 'Every hour'], + [{ cronSchedule: { schedule: 'hourly' } }, 'Every hour'], + [{ cronSchedule: { schedule: 'hours' } }, 'Every hour'], + [{ cronSchedule: { schedule: '@daily' } }, 'Every day'], + [{ cronSchedule: { schedule: 'daily' } }, 'Every day'], + [{ cronSchedule: { schedule: 'days' } }, 'Every day'], + [{ cronSchedule: { schedule: '@weekly' } }, 'Every week'], + [{ cronSchedule: { schedule: 'weekly' } }, 'Every week'], + [{ cronSchedule: { schedule: 'weeks' } }, 'Every week'], + [{ cronSchedule: { schedule: '@monthly' } }, 'Every month'], + [{ cronSchedule: { schedule: 'monthly' } }, 'Every month'], + [{ cronSchedule: { schedule: 'months' } }, 'Every month'], + [{ cronSchedule: { schedule: '@yearly' } }, 'Every year'], + [{ cronSchedule: { schedule: 'yearly' } }, 'Every year'], + [{ cronSchedule: { schedule: 'years' } }, 'Every year'], + [{ cronSchedule: { schedule: '@annually' } }, 'Every year'], + [{ cronSchedule: { schedule: 'annually' } }, 'Every year'], [null!, ''], [{ cronSchedule: { schedule: '' } }, ''] ]; From 2a4c11b6732c5059ebd1384178cfc3bd7675028a Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Tue, 20 Oct 2020 21:56:13 +0200 Subject: [PATCH 3/4] feat: remove offset rendering for now --- src/components/Entities/EntitySchedules.tsx | 11 ++--------- src/components/Launch/SchedulesTable.tsx | 17 +---------------- src/components/Launch/constants.ts | 1 - 3 files changed, 3 insertions(+), 26 deletions(-) diff --git a/src/components/Entities/EntitySchedules.tsx b/src/components/Entities/EntitySchedules.tsx index 06e8e5037..3b04b4852 100644 --- a/src/components/Entities/EntitySchedules.tsx +++ b/src/components/Entities/EntitySchedules.tsx @@ -1,9 +1,6 @@ import { Typography } from '@material-ui/core'; import { makeStyles, Theme } from '@material-ui/core/styles'; -import { - getScheduleFrequencyString, - getScheduleOffsetString -} from 'common/formatters'; +import { getScheduleFrequencyString } from 'common/formatters'; import { WaitForData } from 'components/common'; import { useCommonStyles } from 'components/common/styles'; import { useWorkflowSchedules } from 'components/hooks'; @@ -26,11 +23,7 @@ const RenderSchedules: React.FC<{ {launchPlans.map((launchPlan, idx) => { const { schedule } = launchPlan.spec.entityMetadata; const frequencyString = getScheduleFrequencyString(schedule); - const offsetString = getScheduleOffsetString(schedule); - const scheduleString = offsetString - ? `${frequencyString} (offset: ${offsetString})` - : frequencyString; - return
  • {scheduleString}
  • ; + return
  • {frequencyString}
  • ; })} ); diff --git a/src/components/Launch/SchedulesTable.tsx b/src/components/Launch/SchedulesTable.tsx index 2a1a06284..c3e220d5f 100644 --- a/src/components/Launch/SchedulesTable.tsx +++ b/src/components/Launch/SchedulesTable.tsx @@ -1,10 +1,7 @@ import Chip from '@material-ui/core/Chip'; import { makeStyles, Theme } from '@material-ui/core/styles'; import * as classnames from 'classnames'; -import { - getScheduleFrequencyString, - getScheduleOffsetString -} from 'common/formatters'; +import { getScheduleFrequencyString } from 'common/formatters'; import { ListProps } from 'components/common'; import { useCommonStyles } from 'components/common/styles'; import { @@ -57,18 +54,6 @@ export const schedulesTableColumns: KeyedColumnProps[] = [ label: 'frequency', width: schedulesTableColumnsWidths.frequency }, - { - cellDataGetter: ({ rowData }: CellDataGetterParams) => - rowData.spec.entityMetadata.schedule, - cellRenderer: ({ cellData: schedule }: TableCellProps) => - getScheduleOffsetString(schedule), - dataKey: 'closure', - flexGrow: 1, - flexShrink: 0, - key: 'offset', - label: 'offset', - width: schedulesTableColumnsWidths.offset - }, { cellDataGetter: ({ rowData }: CellDataGetterParams) => isLaunchPlanActive(rowData), diff --git a/src/components/Launch/constants.ts b/src/components/Launch/constants.ts index 44e624f39..f558c3668 100644 --- a/src/components/Launch/constants.ts +++ b/src/components/Launch/constants.ts @@ -7,6 +7,5 @@ export const launchPlansTableColumnWidths = { export const schedulesTableColumnsWidths = { active: 80, frequency: 300, - offset: 80, name: 250 }; From a7150c7fa952671aa1f0d5c5229940f70d720d0a Mon Sep 17 00:00:00 2001 From: Hongxin Liang Date: Wed, 21 Oct 2020 10:03:53 +0200 Subject: [PATCH 4/4] feat: render offset --- src/common/formatters.ts | 52 ++++++++++++++++++++- src/common/test/formatters.spec.ts | 35 +++++++++++++- src/components/Entities/EntitySchedules.tsx | 11 ++++- src/components/Launch/constants.ts | 1 + 4 files changed, 94 insertions(+), 5 deletions(-) diff --git a/src/common/formatters.ts b/src/common/formatters.ts index 1bc9147b6..89ed03e3c 100644 --- a/src/common/formatters.ts +++ b/src/common/formatters.ts @@ -93,6 +93,54 @@ export function millisecondsToHMS(valueMS: number): string { return parts.length ? parts.join(' ') : unknownValueString; } +/** Outputs a value in moment.Duration in (Y M D H M S) format (ex. 1y 1M 1d 2h 3m 30s) */ +export function durationToYMWDHMS(duration: moment.Duration): string { + if (duration.asSeconds() === 0) { + return ''; + } + + const parts = []; + + if (duration.years() !== 0) { + parts.push(`${Math.abs(duration.years())}y`); + } + + if (duration.months() !== 0) { + parts.push(`${Math.abs(duration.months())}M`); + } + + // ISO-8601 does not permit mixing between the PnYnMnD and PnW formats. + // Any week-based input is multiplied by 7 and treated as a number of days. + // However moment can parse the mixture resulting both a number of weeks and a number of days. + // For example both P8D and P1W1D result duration.weeks() == 1 and duration.days() == 8. + // Here we skip showing weeks and only take the total number of days. + if (duration.days() !== 0) { + parts.push(`${Math.abs(duration.days())}d`); + } + + if (duration.hours() !== 0) { + parts.push(`${Math.abs(duration.hours())}h`); + } + + if (duration.minutes() !== 0) { + parts.push(`${Math.abs(duration.minutes())}m`); + } + + if (duration.seconds() !== 0) { + parts.push(`${Math.abs(duration.seconds())}s`); + } + + const now = moment(); + const sign = now + .clone() + .add(duration) + .isBefore(now) + ? '-' + : '+'; + + return `(${sign}) ${parts.join(' ')}`; +} + /** Converts a protobuf Duration value to (H M S) format (ex. 2h 3m 30s)*/ export function protobufDurationToHMS(duration: Protobuf.IDuration) { return millisecondsToHMS(durationToMilliseconds(duration)); @@ -173,8 +221,8 @@ export function getScheduleOffsetString(schedule?: Admin.ISchedule) { if (schedule == null) { return ''; } - if (schedule.cronSchedule) { - return schedule.cronSchedule.offset; + if (schedule.cronSchedule && schedule.cronSchedule.offset) { + return durationToYMWDHMS(moment.duration(schedule.cronSchedule.offset)); } return ''; } diff --git a/src/common/test/formatters.spec.ts b/src/common/test/formatters.spec.ts index 0e3879e72..c9be7bef0 100644 --- a/src/common/test/formatters.spec.ts +++ b/src/common/test/formatters.spec.ts @@ -1,5 +1,6 @@ import { millisecondsToDuration } from 'common/utils'; import { Admin } from 'flyteidl'; +import * as moment from 'moment-timezone'; import { subSecondString, unknownValueString, @@ -9,6 +10,7 @@ import { dateDiffString, dateFromNow, dateWithFromNow, + durationToYMWDHMS, ensureUrlWithProtocol, formatDate, formatDateLocalTimezone, @@ -163,6 +165,36 @@ describe('millisecondsToHMS', () => { ); }); +describe('durationToYMWDHMS', () => { + // input and expected result + const cases: [string, string][] = [ + ['P1Y1M1W1D', '(+) 1y 1M 8d'], + ['P1Y1M1W1DT1H1M1S', '(+) 1y 1M 8d 1h 1m 1s'], + ['P1Y1M1DT1H1M1S', '(+) 1y 1M 1d 1h 1m 1s'], + ['P1M1DT1H1M1S', '(+) 1M 1d 1h 1m 1s'], + ['P1DT1H1M1S', '(+) 1d 1h 1m 1s'], + ['PT1H1M1S', '(+) 1h 1m 1s'], + ['PT1M1S', '(+) 1m 1s'], + ['PT1S', '(+) 1s'], + ['PT1M-1S', '(+) 59s'], + ['-P1Y1M1W1D', '(-) 1y 1M 8d'], + ['-P1Y1M1W1DT1H1M1S', '(-) 1y 1M 8d 1h 1m 1s'], + ['-P1Y1M1DT1H1M1S', '(-) 1y 1M 1d 1h 1m 1s'], + ['-P1M1DT1H1M1S', '(-) 1M 1d 1h 1m 1s'], + ['-P1DT1H1M1S', '(-) 1d 1h 1m 1s'], + ['-PT1H1M1S', '(-) 1h 1m 1s'], + ['-PT1M1S', '(-) 1m 1s'], + ['-PT1S', '(-) 1s'], + ['PT-1M1S', '(-) 59s'], + ['', ''] + ]; + cases.forEach(([input, expected]) => + it(`should produce ${expected} with input ${input}`, () => { + expect(durationToYMWDHMS(moment.duration(input))).toEqual(expected); + }) + ); +}); + describe('getScheduleFrequencyString', () => { // input and expected result const cases: [Admin.ISchedule, string][] = [ @@ -203,7 +235,8 @@ describe('getScheduleFrequencyString', () => { describe('getScheduleOffsetString', () => { // input and expected result const cases: [Admin.ISchedule, string][] = [ - [{ cronSchedule: { offset: 'P1D' } }, 'P1D'], + [{ cronSchedule: { offset: 'P1D' } }, '(+) 1d'], + [{ cronSchedule: { offset: 'P-1D' } }, '(-) 1d'], [null!, ''], [{ cronSchedule: { offset: '' } }, ''] ]; diff --git a/src/components/Entities/EntitySchedules.tsx b/src/components/Entities/EntitySchedules.tsx index 3b04b4852..a07943d00 100644 --- a/src/components/Entities/EntitySchedules.tsx +++ b/src/components/Entities/EntitySchedules.tsx @@ -1,6 +1,9 @@ import { Typography } from '@material-ui/core'; import { makeStyles, Theme } from '@material-ui/core/styles'; -import { getScheduleFrequencyString } from 'common/formatters'; +import { + getScheduleFrequencyString, + getScheduleOffsetString +} from 'common/formatters'; import { WaitForData } from 'components/common'; import { useCommonStyles } from 'components/common/styles'; import { useWorkflowSchedules } from 'components/hooks'; @@ -23,7 +26,11 @@ const RenderSchedules: React.FC<{ {launchPlans.map((launchPlan, idx) => { const { schedule } = launchPlan.spec.entityMetadata; const frequencyString = getScheduleFrequencyString(schedule); - return
  • {frequencyString}
  • ; + const offsetString = getScheduleOffsetString(schedule); + const scheduleString = offsetString + ? `${frequencyString} (offset by ${offsetString})` + : frequencyString; + return
  • {scheduleString}
  • ; })} ); diff --git a/src/components/Launch/constants.ts b/src/components/Launch/constants.ts index f558c3668..02e295f70 100644 --- a/src/components/Launch/constants.ts +++ b/src/components/Launch/constants.ts @@ -7,5 +7,6 @@ export const launchPlansTableColumnWidths = { export const schedulesTableColumnsWidths = { active: 80, frequency: 300, + offset: 300, name: 250 };