diff --git a/src/decorators/is-date.spec.ts b/src/decorators/is-date.spec.ts index 373a9e9..c671dab 100644 --- a/src/decorators/is-date.spec.ts +++ b/src/decorators/is-date.spec.ts @@ -77,6 +77,46 @@ describe('IsDate', () => { Result.ok(make(TestNullable, { date: null })) ); }); + + describe('with default value', () => { + const DEFAULT_DATE = new Date(); + const DEFAULT_DATE_STR = DEFAULT_DATE.toISOString().split('T')[0]; + + class TestOptional { + @IsDate({ + format: 'date', + optional: true, + example: [DEFAULT_DATE, DEFAULT_DATE], + default: DEFAULT_DATE, + }) + date?: Date; + } + + it('generates correct schema', async () => { + expect(await generateSchemas([TestOptional])).toStrictEqual({ + TestOptional: { + type: 'object', + properties: { + date: { + type: 'string', + format: 'date', + default: DEFAULT_DATE_STR, + example: [DEFAULT_DATE_STR, DEFAULT_DATE_STR], + }, + }, + }, + }); + }); + + it('transforms to and from plain', async () => { + const dto = make(TestOptional, {}); + expect(output(dto)).toStrictEqual({ date: DEFAULT_DATE_STR }); + + expect(await input(TestOptional, {})).toStrictEqual( + Result.ok(make(TestOptional, { date: new Date(DEFAULT_DATE_STR) })) + ); + }); + }); }); describe('single date-time', () => { @@ -152,5 +192,44 @@ describe('IsDate', () => { Result.ok(make(TestNullable, { date: null })) ); }); + + describe('with default value', () => { + const DEFAULT_DATE = new Date(); + + class TestOptional { + @IsDate({ + format: 'date-time', + optional: true, + example: DEFAULT_DATE, + default: DEFAULT_DATE, + }) + date?: Date; + } + + it('generates correct schema', async () => { + expect(await generateSchemas([TestOptional])).toStrictEqual({ + TestOptional: { + type: 'object', + properties: { + date: { + type: 'string', + format: 'date-time', + default: DEFAULT_DATE.toISOString(), + example: DEFAULT_DATE.toISOString(), + }, + }, + }, + }); + }); + + it('transforms to and from plain', async () => { + const dto = make(TestOptional, {}); + expect(output(dto)).toStrictEqual({ date: DEFAULT_DATE.toISOString() }); + + expect(await input(TestOptional, {})).toStrictEqual( + Result.ok(make(TestOptional, { date: DEFAULT_DATE })) + ); + }); + }); }); }); diff --git a/src/decorators/is-date.ts b/src/decorators/is-date.ts index 4603961..6193dde 100644 --- a/src/decorators/is-date.ts +++ b/src/decorators/is-date.ts @@ -9,18 +9,27 @@ const dateRegex = /^\d{4}-\d{2}-\d{2}$/; // TODO: array support export const IsDate = ({ format, + default: def, + example, ...base -}: Omit, 'isArray'>): PropertyDecorator => - compose( +}: Omit, 'isArray'>): PropertyDecorator => { + const dateTime = format === 'date-time'; + + return compose( { type: 'string', format }, - base, - TransformHandlingOptional(base, format === 'date' ? transformDate : transformDateTime), + { ...base, default: stringify(def, dateTime), example: stringify(example, dateTime) }, + TransformHandlingOptional(base, dateTime ? transformDateTime : transformDate), IsDateCV({ message: ({ value }) => value?.message }) ); +}; function transformDate({ key, value, type }: TransformFnParams) { if (type === TransformationType.CLASS_TO_PLAIN) { - return value.toISOString().split('T')[0]; + if (typeof value === 'string') { + return value; + } + + return dateToString(value); } if (!dateRegex.test(value)) { @@ -37,7 +46,11 @@ function transformDate({ key, value, type }: TransformFnParams) { function transformDateTime({ key, value, type }: TransformFnParams) { if (type === TransformationType.CLASS_TO_PLAIN) { - return value.toISOString(); + if (typeof value === 'string') { + return value; + } + + return dateTimeToString(value); } if (!isDateString(value, { strict: true })) { @@ -46,3 +59,26 @@ function transformDateTime({ key, value, type }: TransformFnParams) { return new Date(value); } + +function stringify( + value: Date | Date[] | undefined, + dateTime: boolean +): string | string[] | undefined { + if (value === undefined) { + return value; + } + + if (value instanceof Date) { + return dateTime ? dateTimeToString(value) : dateToString(value); + } + + return value.map((v) => stringify(v, dateTime) as string); +} + +function dateTimeToString(date: Date): string { + return date.toISOString(); +} + +function dateToString(date: Date): string { + return date.toISOString().split('T')[0]; +}