fix: let user choice 'standard' vs 'aws'
Let the user choose between cron format (since help is different)
This commit is contained in:
		
							parent
							
								
									8754d626e1
								
							
						
					
					
						commit
						fa01008dc8
					
				
							
								
								
									
										5
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -135,13 +135,18 @@ declare module '@vue/runtime-core' { | |||||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] |     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||||
|     NDivider: typeof import('naive-ui')['NDivider'] |     NDivider: typeof import('naive-ui')['NDivider'] | ||||||
|     NEllipsis: typeof import('naive-ui')['NEllipsis'] |     NEllipsis: typeof import('naive-ui')['NEllipsis'] | ||||||
|  |     NForm: typeof import('naive-ui')['NForm'] | ||||||
|  |     NFormItem: typeof import('naive-ui')['NFormItem'] | ||||||
|     NH1: typeof import('naive-ui')['NH1'] |     NH1: typeof import('naive-ui')['NH1'] | ||||||
|     NH3: typeof import('naive-ui')['NH3'] |     NH3: typeof import('naive-ui')['NH3'] | ||||||
|     NIcon: typeof import('naive-ui')['NIcon'] |     NIcon: typeof import('naive-ui')['NIcon'] | ||||||
|     NLayout: typeof import('naive-ui')['NLayout'] |     NLayout: typeof import('naive-ui')['NLayout'] | ||||||
|     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] |     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] | ||||||
|     NMenu: typeof import('naive-ui')['NMenu'] |     NMenu: typeof import('naive-ui')['NMenu'] | ||||||
|  |     NRadio: typeof import('naive-ui')['NRadio'] | ||||||
|  |     NRadioGroup: typeof import('naive-ui')['NRadioGroup'] | ||||||
|     NSpace: typeof import('naive-ui')['NSpace'] |     NSpace: typeof import('naive-ui')['NSpace'] | ||||||
|  |     NSwitch: typeof import('naive-ui')['NSwitch'] | ||||||
|     NTable: typeof import('naive-ui')['NTable'] |     NTable: typeof import('naive-ui')['NTable'] | ||||||
|     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] |     NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] | ||||||
|     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] |     OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default'] | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ import { getCronType, getLastExecutionTimes, isCronValid } from './crontab-gener | |||||||
| describe('crontab-generator', () => { | describe('crontab-generator', () => { | ||||||
|   describe('isCronValid', () => { |   describe('isCronValid', () => { | ||||||
|     it('should return true for all valid formats', () => { |     it('should return true for all valid formats', () => { | ||||||
|  |       // standard format
 | ||||||
|       expect(isCronValid('0 0 * * 1-5')).toBe(true); |       expect(isCronValid('0 0 * * 1-5')).toBe(true); | ||||||
|       expect(isCronValid('23 0-20/2 * * *')).toBe(true); |       expect(isCronValid('23 0-20/2 * * *')).toBe(true); | ||||||
| 
 | 
 | ||||||
| @ -11,6 +12,24 @@ describe('crontab-generator', () => { | |||||||
|       expect(isCronValid('0 11-22 ? * MON-FRI *')).toBe(true); |       expect(isCronValid('0 11-22 ? * MON-FRI *')).toBe(true); | ||||||
|       expect(isCronValid('0 0 ? * 1 *')).toBe(true); |       expect(isCronValid('0 0 ? * 1 *')).toBe(true); | ||||||
|     }); |     }); | ||||||
|  |     it('should check standard format', () => { | ||||||
|  |       // standard format
 | ||||||
|  |       expect(isCronValid('0 0 * * 1-5', 'standard')).toBe(true); | ||||||
|  |       expect(isCronValid('23 0-20/2 * * *', 'standard')).toBe(true); | ||||||
|  | 
 | ||||||
|  |       // AWS format
 | ||||||
|  |       expect(isCronValid('0 11-22 ? * MON-FRI *', 'standard')).toBe(false); | ||||||
|  |       expect(isCronValid('0 0 ? * 1 *', 'standard')).toBe(false); | ||||||
|  |     }); | ||||||
|  |     it('should check aws format', () => { | ||||||
|  |       // standard format
 | ||||||
|  |       expect(isCronValid('0 0 * * 1-5', 'aws')).toBe(false); | ||||||
|  |       expect(isCronValid('23 0-20/2 * * *', 'aws')).toBe(false); | ||||||
|  | 
 | ||||||
|  |       // AWS format
 | ||||||
|  |       expect(isCronValid('0 11-22 ? * MON-FRI *', 'aws')).toBe(true); | ||||||
|  |       expect(isCronValid('0 0 ? * 1 *', 'aws')).toBe(true); | ||||||
|  |     }); | ||||||
|     it('should return false for all invalid formats', () => { |     it('should return false for all invalid formats', () => { | ||||||
|       expect(isCronValid('aert')).toBe(false); |       expect(isCronValid('aert')).toBe(false); | ||||||
|       expect(isCronValid('40 *')).toBe(false); |       expect(isCronValid('40 *')).toBe(false); | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| import { parseExpression } from 'cron-parser'; | import { parseExpression } from 'cron-parser'; | ||||||
| import EventCronParser from 'event-cron-parser'; | import EventCronParser from 'event-cron-parser'; | ||||||
| 
 | 
 | ||||||
|  | export type CronType = 'standard' | 'aws'; | ||||||
|  | 
 | ||||||
| export function getLastExecutionTimes(cronExpression: string, tz: string | undefined = undefined, count: number = 5) { | export function getLastExecutionTimes(cronExpression: string, tz: string | undefined = undefined, count: number = 5) { | ||||||
|   if (getCronType(cronExpression) === 'standard') { |   if (getCronType(cronExpression) === 'standard') { | ||||||
|     const interval = parseExpression(cronExpression, { tz }); |     const interval = parseExpression(cronExpression, { tz }); | ||||||
| @ -22,18 +24,19 @@ export function getLastExecutionTimes(cronExpression: string, tz: string | undef | |||||||
|   return []; |   return []; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function isCronValid(v: string) { | export function isCronValid(cronExpression: string, cronType: CronType | 'any' = 'any') { | ||||||
|   return !!getCronType(v); |   const expressionCronType = getCronType(cronExpression); | ||||||
|  |   return cronType === 'any' ? !!expressionCronType : expressionCronType === cronType; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function getCronType(v: string) { | export function getCronType(cronExpression: string) { | ||||||
|   try { |   try { | ||||||
|     parseExpression(v); |     parseExpression(cronExpression); | ||||||
|     return 'standard'; |     return 'standard'; | ||||||
|   } |   } | ||||||
|   catch (_) { |   catch (_) { | ||||||
|     try { |     try { | ||||||
|       const parsed = new EventCronParser(v); |       const parsed = new EventCronParser(cronExpression); | ||||||
|       parsed.validate(); |       parsed.validate(); | ||||||
|       return 'aws'; |       return 'aws'; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ | |||||||
| import cronstrue from 'cronstrue'; | import cronstrue from 'cronstrue'; | ||||||
| import ctz from 'countries-and-timezones'; | import ctz from 'countries-and-timezones'; | ||||||
| import getTimezoneOffset from 'get-timezone-offset'; | import getTimezoneOffset from 'get-timezone-offset'; | ||||||
| import { getCronType, getLastExecutionTimes, isCronValid } from './crontab-generator.service'; | import { type CronType, getLastExecutionTimes, isCronValid } from './crontab-generator.service'; | ||||||
| import { useStyleStore } from '@/stores/style.store'; | import { useStyleStore } from '@/stores/style.store'; | ||||||
| import { useQueryParamOrStorage } from '@/composable/queryParams'; | import { useQueryParamOrStorage } from '@/composable/queryParams'; | ||||||
| 
 | 
 | ||||||
| @ -20,10 +20,13 @@ const cronstrueConfig = reactive({ | |||||||
| 
 | 
 | ||||||
| // getTimezoneOffset(tz.name, now) / 60 | // getTimezoneOffset(tz.name, now) / 60 | ||||||
| const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; | const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; | ||||||
| const allTimezones = Object.values(ctz.getAllTimezones()).map(tz => ({ | const allTimezones = Object.values(ctz.getAllTimezones()).map((tz) => { | ||||||
|  |   const timezoneUTCDSTOffset = tz.utcOffset === tz.dstOffset ? tz.utcOffsetStr : `${tz.utcOffsetStr}/${tz.dstOffsetStr}`; | ||||||
|  |   return { | ||||||
|     value: tz.name, |     value: tz.name, | ||||||
|   label: `${tz.name === browserTimezone ? 'Browser TZ - ' : ''}${tz.name} (${tz.utcOffset === tz.dstOffset ? tz.utcOffsetStr : `${tz.utcOffsetStr}/${tz.dstOffsetStr}`})`, |     label: `${tz.name === browserTimezone ? 'Browser TZ - ' : ''}${tz.name} (${timezoneUTCDSTOffset})`, | ||||||
| })); |   }; | ||||||
|  | }); | ||||||
| const currentTimezone = useQueryParamOrStorage({ name: 'tz', storageName: 'crongen:tz', defaultValue: browserTimezone }); | const currentTimezone = useQueryParamOrStorage({ name: 'tz', storageName: 'crongen:tz', defaultValue: browserTimezone }); | ||||||
| watchEffect(() => { | watchEffect(() => { | ||||||
|   cronstrueConfig.tzOffset = -getTimezoneOffset(currentTimezone.value, new Date()) / 60; |   cronstrueConfig.tzOffset = -getTimezoneOffset(currentTimezone.value, new Date()) / 60; | ||||||
| @ -136,19 +139,24 @@ const awsHelpers = [ | |||||||
|   }, |   }, | ||||||
| ]; | ]; | ||||||
| 
 | 
 | ||||||
| const cronType = computed({ | const defaultAWSCronExpression = '0 0 ? * 1 *'; | ||||||
|   get() { | const defaultStandardCronExpression = '40 * * * *'; | ||||||
|     return getCronType(cron.value); | const cronType = ref<CronType>('standard'); | ||||||
|   }, | watch(cronType, | ||||||
|   set(newCronType) { |   (newCronType) => { | ||||||
|     if (newCronType === 'aws') { |     if (newCronType === 'aws') { | ||||||
|       cron.value = '0 0 ? * 1 *'; |       if (!cron.value || cron.value === defaultStandardCronExpression) { | ||||||
|  |         cron.value = defaultAWSCronExpression; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     else if (newCronType === 'standard') { | ||||||
|  |       if (!cron.value || cron.value === defaultAWSCronExpression) { | ||||||
|  |         cron.value = defaultStandardCronExpression; | ||||||
|       } |       } | ||||||
|     else { |  | ||||||
|       cron.value = '40 * * * *'; |  | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
| }); | ); | ||||||
|  | 
 | ||||||
| const getHelpers = computed(() => { | const getHelpers = computed(() => { | ||||||
|   if (cronType.value === 'aws') { |   if (cronType.value === 'aws') { | ||||||
|     return awsHelpers; |     return awsHelpers; | ||||||
| @ -165,7 +173,7 @@ const cronString = computed(() => { | |||||||
| 
 | 
 | ||||||
| const cronValidationRules = [ | const cronValidationRules = [ | ||||||
|   { |   { | ||||||
|     validator: (value: string) => isCronValid(value), |     validator: (value: string) => isCronValid(value, cronType.value), | ||||||
|     message: 'This cron is invalid', |     message: 'This cron is invalid', | ||||||
|   }, |   }, | ||||||
| ]; | ]; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user