From 0badb90525d114f8ffb62cbb99af813b04d9f77d Mon Sep 17 00:00:00 2001 From: Imad <118115991+Imadnajam@users.noreply.github.com> Date: Tue, 15 Apr 2025 11:44:21 +0100 Subject: [PATCH 1/2] feat(backup-time): add defaults, validation fixes, and parsing for DailyAutomaticBackupStartTime --- .../lib/daily-automatic-backup-start-time.ts | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.ts b/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.ts index ef9229a62ccf8..5c7373ec9c767 100644 --- a/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.ts +++ b/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.ts @@ -6,12 +6,14 @@ import { UnscopedValidationError } from '../../core'; export interface DailyAutomaticBackupStartTimeProps { /** * The hour of the day (from 0-23) for automatic backup starts. + * @default 22 */ - readonly hour: number; + readonly hour?: number; /** * The minute of the hour (from 0-59) for automatic backup starts. + * @default 0 */ - readonly minute: number; + readonly minute?: number; } /** @@ -21,50 +23,71 @@ export class DailyAutomaticBackupStartTime { /** * The start hour of the automatic backup time in Coordinated Universal Time (UTC), using 24-hour time. * For example, 17 refers to 5:00 P.M. UTC. - * - * @default - 22 */ private readonly hour: string; /** * The start minute of the automatic backup time, in UTC. - * - * @default - 0 */ private readonly minute: string; - constructor(props: DailyAutomaticBackupStartTimeProps) { - this.validate(props.hour, props.minute); + /** + * Creates a new DailyAutomaticBackupStartTime instance. + * @param props Configuration properties (defaults to hour:22, minute:0) + */ + constructor(props: DailyAutomaticBackupStartTimeProps = {}) { + const hour = props.hour ?? 22; + const minute = props.minute ?? 0; + this.validate(hour, minute); + + this.hour = this.getTwoDigitString(hour); + this.minute = this.getTwoDigitString(minute); + } + + /** + * Creates an instance from a "HH:MM" formatted string. + * @param timeString Time string in "HH:MM" format (24-hour) + * @returns New DailyAutomaticBackupStartTime instance + * @throws UnscopedValidationError if format is invalid + */ + public static fromString(timeString: string): DailyAutomaticBackupStartTime { + const timeRegex = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/; + if (!timeRegex.test(timeString)) { + throw new UnscopedValidationError(`Time must be in "HH:MM" format (24-hour). Received: ${timeString}`); + } - this.hour = this.getTwoDigitString(props.hour); - this.minute = this.getTwoDigitString(props.minute); + const [hour, minute] = timeString.split(':').map(Number); + return new DailyAutomaticBackupStartTime({ hour, minute }); } + /** - * Converts an hour, and minute into HH:MM string. + * Converts the time to "HH:MM" string format. + * @returns Time string in "HH:MM" format */ public toTimestamp(): string { return `${this.hour}:${this.minute}`; } /** - * Pad an integer so that it always contains at least 2 digits. Assumes the number is a positive integer. + * Pad an integer to 2 digits. Assumes the number is a positive integer. + * @param n Number to pad + * @returns 2-digit string representation */ private getTwoDigitString(n: number): string { - const numberString = n.toString(); - if (numberString.length === 1) { - return `0${n}`; - } - return numberString; + return n.toString().padStart(2, '0'); } /** - * Validation needed for the values of the daily automatic backup time. + * Validates hour and minute values. + * @param hour Hour value (0-23) + * @param minute Minute value (0-59) + * @throws UnscopedValidationError if validation fails */ - private validate(hour: number, minute: number) { + private validate(hour: number, minute: number): void { if (!Number.isInteger(hour) || hour < 0 || hour > 23) { - throw new UnscopedValidationError(`dailyAutomaticBackupStartTime hour must be an integer between 0 and 24. received: ${hour}`); + throw new UnscopedValidationError(`hour must be an integer between 0 and 23. Received: ${hour}`); } if (!Number.isInteger(minute) || minute < 0 || minute > 59) { - throw new UnscopedValidationError(`dailyAutomaticBackupStartTime minute must be an integer between 0 and 59. received: ${minute}`); + throw new UnscopedValidationError(`minute must be an integer between 0 and 59. Received: ${minute}`); } } } From 9a07df3c23d8582c93d539cb6276e58758352e12 Mon Sep 17 00:00:00 2001 From: Imad <118115991+Imadnajam@users.noreply.github.com> Date: Tue, 15 Apr 2025 11:45:43 +0100 Subject: [PATCH 2/2] daily-automatic-backup-start-time.test Added proper default values (hour:22, minute:0) Fixed validation error message for hour Added fromString static method for parsing time strings Improved JSDoc documentation Added comprehensive unit tests Simplified getTwoDigitString using padStart Made props optional in the interface --- .../daily-automatic-backup-start-time.test.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.test.ts diff --git a/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.test.ts b/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.test.ts new file mode 100644 index 0000000000000..8ad91319fa524 --- /dev/null +++ b/packages/aws-cdk-lib/aws-fsx/lib/daily-automatic-backup-start-time.test.ts @@ -0,0 +1,53 @@ +import { DailyAutomaticBackupStartTime } from './daily-automatic-backup-start-time'; +import { UnscopedValidationError } from '../../core'; + +describe('DailyAutomaticBackupStartTime', () => { + describe('constructor', () => { + it('should use default values when none provided', () => { + const time = new DailyAutomaticBackupStartTime(); + expect(time.toTimestamp()).toBe('22:00'); + }); + + it('should accept valid time values', () => { + const time = new DailyAutomaticBackupStartTime({ hour: 9, minute: 30 }); + expect(time.toTimestamp()).toBe('09:30'); + }); + + it('should pad single-digit values', () => { + const time = new DailyAutomaticBackupStartTime({ hour: 5, minute: 7 }); + expect(time.toTimestamp()).toBe('05:07'); + }); + + it('should throw for invalid hour', () => { + expect(() => new DailyAutomaticBackupStartTime({ hour: 24 })).toThrow(UnscopedValidationError); + expect(() => new DailyAutomaticBackupStartTime({ hour: -1 })).toThrow(UnscopedValidationError); + }); + + it('should throw for invalid minute', () => { + expect(() => new DailyAutomaticBackupStartTime({ minute: 60 })).toThrow(UnscopedValidationError); + expect(() => new DailyAutomaticBackupStartTime({ minute: -1 })).toThrow(UnscopedValidationError); + }); + }); + + describe('fromString', () => { + it('should parse valid time strings', () => { + expect(DailyAutomaticBackupStartTime.fromString('00:00').toTimestamp()).toBe('00:00'); + expect(DailyAutomaticBackupStartTime.fromString('23:59').toTimestamp()).toBe('23:59'); + expect(DailyAutomaticBackupStartTime.fromString('09:05').toTimestamp()).toBe('09:05'); + }); + + it('should throw for invalid formats', () => { + expect(() => DailyAutomaticBackupStartTime.fromString('24:00')).toThrow(UnscopedValidationError); + expect(() => DailyAutomaticBackupStartTime.fromString('12:60')).toThrow(UnscopedValidationError); + expect(() => DailyAutomaticBackupStartTime.fromString('abc')).toThrow(UnscopedValidationError); + expect(() => DailyAutomaticBackupStartTime.fromString('12:00:00')).toThrow(UnscopedValidationError); + }); + }); + + describe('toTimestamp', () => { + it('should return properly formatted string', () => { + expect(new DailyAutomaticBackupStartTime({ hour: 1, minute: 2 }).toTimestamp()).toBe('01:02'); + expect(new DailyAutomaticBackupStartTime({ hour: 10, minute: 20 }).toTimestamp()).toBe('10:20'); + }); + }); +});