Skip to content

Commit

Permalink
feat: Add support for date validation
Browse files Browse the repository at this point in the history
  • Loading branch information
l0kal committed Sep 30, 2021
1 parent 6ebadee commit 1fce6ca
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 0 deletions.
84 changes: 84 additions & 0 deletions src/decorators/is-date.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Result } from 'true-myth';

import { input, make, output } from '../../tests/helpers';
import { IsDate } from '../nestjs-swagger-dto';

describe('IsDate', () => {
describe('single date', () => {
class Test {
@IsDate({ format: 'date' })
date!: Date;
}

it('accepts date', async () => {
expect(await input(Test, { date: '2011-12-30' })).toStrictEqual(
Result.ok(make(Test, { date: new Date('2011-12-30') }))
);
});

it('rejects invalid date', async () => {
expect(await input(Test, { date: '2011-13-13' })).toStrictEqual(
Result.err('date is not a valid Date')
);

expect(await input(Test, { date: 'not a date' })).toStrictEqual(
Result.err('date is not formatted as `yyyy-mm-dd`')
);

expect(await input(Test, { date: '2011/12/02' })).toStrictEqual(
Result.err('date is not formatted as `yyyy-mm-dd`')
);
});

// it('transforms to plain', async () => {
// const dto = make(Test, { booleanField: true });
// expect(output(dto)).toStrictEqual({ booleanField: true });
// });

// it('rejects everything else', async () => {
// const testValues: unknown[] = [
// { booleanField: 'true' },
// { booleanField: 'false' },
// { booleanField: 'abc' },
// { booleanField: 0 },
// { booleanField: [] },
// { booleanField: {} },
// { booleanField: null },
// {},
// ];

// for (const testValue of testValues) {
// expect(await input(Test, testValue)).toStrictEqual(
// Result.err('booleanField must be a boolean value')
// );
// }
// });
});

describe('single date-time', () => {
class Test {
@IsDate({ format: 'date-time' })
date!: Date;
}

it('accepts date-time', async () => {
expect(await input(Test, { date: '2017-06-01T18:43:26.000Z' })).toStrictEqual(
Result.ok(make(Test, { date: new Date('2017-06-01T18:43:26.000Z') }))
);
});

it('rejects invalid date-time', async () => {
expect(await input(Test, { date: '2011-13-13' })).toStrictEqual(
Result.err('date is not ISO8601 format')
);

expect(await input(Test, { date: 'not a date' })).toStrictEqual(
Result.err('date is not ISO8601 format')
);

expect(await input(Test, { date: '2011/12/02' })).toStrictEqual(
Result.err('date is not ISO8601 format')
);
});
});
});
37 changes: 37 additions & 0 deletions src/decorators/is-date.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Transform } from 'class-transformer';
import { IsDate as IsDateCV, isDateString } from 'class-validator';

import { Base, compose, noop } from '../core';

const crudeDateRegex = /^\d{4}-\d{2}-\d{2}$/;

// TODO: array support
export const IsDate = ({
format,
...base
}: Omit<Base<Date>, 'isArray'> & { format: 'date' | 'date-time' }): PropertyDecorator =>
compose(
{ type: 'string', format },
base,
format === 'date'
? Transform(({ key, value }) => {
if (!crudeDateRegex.test(value)) {
return new Error(`${key} is not formatted as \`yyyy-mm-dd\``);
}

const date = new Date(value);
if (isNaN(date.getTime())) {
return new Error(`${key} is not a valid Date`);
}

return date;
})
: Transform(({ key, value }) => {
if (!isDateString(value, { strict: true })) {
return new Error(`${key} is not ISO8601 format`);
}

return new Date(value);
}),
IsDateCV({ message: ({ value }) => value.message })
);
1 change: 1 addition & 0 deletions src/nestjs-swagger-dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './decorators/is-boolean';
export * from './decorators/is-constant';
export * from './decorators/is-date';
export * from './decorators/is-enum';
export * from './decorators/is-nested';
export * from './decorators/is-number';
Expand Down

0 comments on commit 1fce6ca

Please sign in to comment.