From f51dc204fcc66aad26172bc3c9c243536b1264ed Mon Sep 17 00:00:00 2001 From: Aris Kemper Date: Wed, 27 Dec 2023 11:34:07 +0100 Subject: [PATCH] feat: ISSN validation --- library/src/validations/index.ts | 1 + library/src/validations/issn/index.ts | 1 + library/src/validations/issn/issn.test.ts | 47 +++++++++++++++ library/src/validations/issn/issn.ts | 58 +++++++++++++++++++ .../routes/api/(validations)/issn/index.mdx | 9 +++ website/src/routes/api/menu.md | 1 + .../(main-concepts)/pipelines/index.mdx | 1 + 7 files changed, 118 insertions(+) create mode 100644 library/src/validations/issn/index.ts create mode 100644 library/src/validations/issn/issn.test.ts create mode 100644 library/src/validations/issn/issn.ts create mode 100644 website/src/routes/api/(validations)/issn/index.mdx diff --git a/library/src/validations/index.ts b/library/src/validations/index.ts index c9c1d5233..b058c7650 100644 --- a/library/src/validations/index.ts +++ b/library/src/validations/index.ts @@ -24,6 +24,7 @@ export * from './isoTime/index.ts'; export * from './isoTimeSecond/index.ts'; export * from './isoTimestamp/index.ts'; export * from './isoWeek/index.ts'; +export * from './issn/index.ts'; export * from './length/index.ts'; export * from './mac/index.ts'; export * from './mac48/index.ts'; diff --git a/library/src/validations/issn/index.ts b/library/src/validations/issn/index.ts new file mode 100644 index 000000000..500472862 --- /dev/null +++ b/library/src/validations/issn/index.ts @@ -0,0 +1 @@ +export * from './issn.ts'; diff --git a/library/src/validations/issn/issn.test.ts b/library/src/validations/issn/issn.test.ts new file mode 100644 index 000000000..d1b016565 --- /dev/null +++ b/library/src/validations/issn/issn.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, test } from 'vitest'; +import { issn } from './issn.ts'; + +describe('issn', () => { + test('should pass only valid ISSN', () => { + const validate = issn(); + + const value1 = '2049-3630'; + expect(validate._parse(value1).output).toBe(value1); + const value2 = '0317-8471'; + expect(validate._parse(value2).output).toBe(value2); + const value3 = '0378-5955'; + expect(validate._parse(value3).output).toBe(value3); + const value4 = '2434-561X'; + expect(validate._parse(value4).output).toBe(value4); + const value5 = '2434-561x'; + expect(validate._parse(value5).output).toBe(value5); + const value6 = '1037-6178'; + expect(validate._parse(value6).output).toBe(value6); + const value7 = '01896016'; + expect(validate._parse(value7).output).toBe(value7); + const value8 = '20905076'; + expect(validate._parse(value8).output).toBe(value8); + }); + + test('should reject invalid ISSN', () => { + const validate = issn(); + + expect(validate._parse('').issues).toBeTruthy(); + expect(validate._parse('0000-0000').issues).toBeTruthy(); + expect(validate._parse('1234-5678').issues).toBeTruthy(); + expect(validate._parse('abcd-efgh').issues).toBeTruthy(); + expect(validate._parse('1234-567A').issues).toBeTruthy(); + expect(validate._parse('1234-567X').issues).toBeTruthy(); + expect(validate._parse('0').issues).toBeTruthy(); + expect(validate._parse('2434-561c').issues).toBeTruthy(); + expect(validate._parse('1684-5370').issues).toBeTruthy(); + expect(validate._parse('19960791').issues).toBeTruthy(); + expect(validate._parse('2090507&').issues).toBeTruthy(); + }); + + test('should return custom error message', () => { + const error = 'Value is not an ISSN!'; + const validate = issn(error); + expect(validate._parse('test').issues?.[0].message).toBe(error); + }); +}); diff --git a/library/src/validations/issn/issn.ts b/library/src/validations/issn/issn.ts new file mode 100644 index 000000000..1d3f3c8ee --- /dev/null +++ b/library/src/validations/issn/issn.ts @@ -0,0 +1,58 @@ +import type { BaseValidation, ErrorMessage } from '../../types/index.ts'; +import { actionIssue, actionOutput } from '../../utils/index.ts'; + +/** + * ISSN validation type. + */ +export type IssnValidation = BaseValidation & { + /** + * The validation type. + */ + type: 'issn'; + /** + * The ISSN validation function. + */ + requirement: (input: TInput) => boolean; +}; + +/** + * Creates a pipeline validation action that validates an [ISSN](https://en.wikipedia.org/wiki/ISSN). + * + * @param message The error message. + * + * @returns A validation action. + */ +export function issn( + message: ErrorMessage = 'Invalid ISSN' +): IssnValidation { + return { + type: 'issn', + async: false, + message, + requirement: isISSN, + _parse(input) { + return !this.requirement(input) + ? actionIssue(this.type, this.message, input, this.requirement) + : actionOutput(input); + }, + }; +} + +const ISSN_REGEX = /^(?!0{4}-?0{3}[0X])\d{4}-?\d{3}[\dX]$/iu; + +function isISSN(issn: string): boolean { + issn = issn.toUpperCase(); + if (!ISSN_REGEX.test(issn)) { + return false; + } + + const numbers = issn + .replace('-', '') + .split('') + .map((digit, index) => { + return digit === 'X' ? 10 : parseInt(digit, 10) * (8 - index); + }); + + const sum = numbers.reduce((acc, value) => acc + value, 0); + return sum % 11 === 0; +} diff --git a/website/src/routes/api/(validations)/issn/index.mdx b/website/src/routes/api/(validations)/issn/index.mdx new file mode 100644 index 000000000..73c065559 --- /dev/null +++ b/website/src/routes/api/(validations)/issn/index.mdx @@ -0,0 +1,9 @@ +--- +title: issn +contributors: + - ariskemper +--- + +# issn + +> The content of this page is not yet ready. Until then just use the [source code](https://github.com/fabian-hiller/valibot/blob/main/library/src/validations/issn/issn.ts). diff --git a/website/src/routes/api/menu.md b/website/src/routes/api/menu.md index 52c082856..310b974af 100644 --- a/website/src/routes/api/menu.md +++ b/website/src/routes/api/menu.md @@ -88,6 +88,7 @@ - [isoTimeSecond](/api/isoTimeSecond/) - [isoTimestamp](/api/isoTimestamp/) - [isoWeek](/api/isoWeek/) +- [issn](/api/issn/) - [length](/api/length/) - [mac](/api/mac/) - [mac48](/api/mac48/) diff --git a/website/src/routes/guides/(main-concepts)/pipelines/index.mdx b/website/src/routes/guides/(main-concepts)/pipelines/index.mdx index eef965196..ef6bf55d2 100644 --- a/website/src/routes/guides/(main-concepts)/pipelines/index.mdx +++ b/website/src/routes/guides/(main-concepts)/pipelines/index.mdx @@ -68,6 +68,7 @@ Pipeline validation actions examine the input and, if the input does not meet a 'isoTimeSecond', 'isoTimestamp', 'isoWeek', + 'issn', 'length', 'mac', 'mac48',