From 08c7e5c65adae6f1da9ce461c5f5ab0e5cccaff6 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 21:21:44 +0530 Subject: [PATCH] Refactors validations --- tests/domain-utils.test.js | 66 +---------------------------------- tests/domains.test.js | 3 +- tests/validations.test.js | 66 +++++++++++++++++++++++++++++++++++ utils/domain.js | 71 ++------------------------------------ utils/helpers.js | 20 +++++++++++ utils/validations.js | 56 ++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 134 deletions(-) create mode 100644 tests/validations.test.js create mode 100644 utils/helpers.js create mode 100644 utils/validations.js diff --git a/tests/domain-utils.test.js b/tests/domain-utils.test.js index daf298efd..2d6868b8d 100644 --- a/tests/domain-utils.test.js +++ b/tests/domain-utils.test.js @@ -1,4 +1,4 @@ -const { getDomains, validateDomainData } = require('../utils/domain'); +const { getDomains } = require('../utils/domain'); describe('getDomains', () => { it('should resolve with the list of domains', async () => { @@ -7,67 +7,3 @@ describe('getDomains', () => { }); }); -const defaultDomain = { - name: 'aaa', - record: { - A: ['121.121.121.121'] - }, - owner: { - username: 'betsy', - email: 'betsyfuckyoassup@foobar.com', - }, -}; - -const getstroflen = len => Array(len).fill('a').join(''); - -describe('validateDomainData', () => { - const invalidCases = [ - {}, - { name: 'helo' }, - { name: 'wwow', record: { A: ['12312'] } }, - ...['', ' ', undefined, 'hlo wld', 'g32++13', 'ajsdD_123yq', 'khsda%', '122*dsd', getstroflen(101)].map(name => ({ - ...defaultDomain, - name, - })), - { ...defaultDomain, record: { CNAME: 'sd', A: ['121,3213'] } }, - { ...defaultDomain, record: { A: ['121', '12'], FOOBAR: ['sd'] } }, - { ...defaultDomain, record: { A: [] } }, - { ...defaultDomain, record: { A: ['11122'], URL: 'foobar' } }, - { ...defaultDomain, owner: {}, }, - { ...defaultDomain, owner: { username: 'hwelo', }, }, - { ...defaultDomain, owner: { email: 'hwelo' }, }, - { ...defaultDomain, record: { CNAME: 'http://foobar.com' } }, - { ...defaultDomain, record: { CNAME: 'https://foobar.com' } }, - ]; - - const validCases = [ - defaultDomain, - ...['hello', 'hello-world', '11111111111', '--wow--', 'wow--', '--wow'].map(name => ({ - ...defaultDomain, - name, - })), - { - ...defaultDomain, - description: getstroflen(99), - }, - { ...defaultDomain, record: { CNAME: 'aa.sd', URL: '121,3213' } }, - ]; - - it('should return false for invalid data', () => { - invalidCases.forEach(data => { - const { valid, errors } = validateDomainData(data); - expect(valid).toBe(false); - expect(errors.length).toBeGreaterThan(0); - }); - }); - - it('should return true if the name is valid', () => { - validCases.forEach(data => { - const { valid, errors } = validateDomainData(data); - if (!valid) console.log(errors); - expect(valid).toBe(true); - expect(errors).toEqual([]); - }); - }); -}); - diff --git a/tests/domains.test.js b/tests/domains.test.js index 7f219fec4..fd80c3f43 100644 --- a/tests/domains.test.js +++ b/tests/domains.test.js @@ -1,6 +1,7 @@ const R = require('ramda'); const fs = require('fs'); -const { getDomains, validateDomainData } = require('../utils/domain'); +const { getDomains } = require('../utils/domain'); +const { validateDomainData } = require('../utils/validations'); const { DOMAINS_PATH } = require('../utils/constants'); describe('Domains', () => { diff --git a/tests/validations.test.js b/tests/validations.test.js new file mode 100644 index 000000000..057ffc3c8 --- /dev/null +++ b/tests/validations.test.js @@ -0,0 +1,66 @@ +const { validateDomainData } = require('../utils/validations'); + +const defaultDomain = { + name: 'aaa', + record: { + A: ['121.121.121.121'] + }, + owner: { + username: 'betsy', + email: 'betsyfuckyoassup@foobar.com', + }, +}; + +const getstroflen = len => Array(len).fill('a').join(''); + +describe('validateDomainData', () => { + const invalidCases = [ + {}, + { name: 'helo' }, + { name: 'wwow', record: { A: ['12312'] } }, + ...['', ' ', undefined, 'hlo wld', 'g32++13', 'ajsdD_123yq', 'khsda%', '122*dsd', getstroflen(101)].map(name => ({ + ...defaultDomain, + name, + })), + { ...defaultDomain, record: { CNAME: 'sd', A: ['121,3213'] } }, + { ...defaultDomain, record: { A: ['121', '12'], FOOBAR: ['sd'] } }, + { ...defaultDomain, record: { A: [] } }, + { ...defaultDomain, record: { A: ['11122'], URL: 'foobar' } }, + { ...defaultDomain, owner: {}, }, + { ...defaultDomain, owner: { username: 'hwelo', }, }, + { ...defaultDomain, owner: { email: 'hwelo' }, }, + { ...defaultDomain, record: { CNAME: 'http://foobar.com' } }, + { ...defaultDomain, record: { CNAME: 'https://foobar.com' } }, + ]; + + const validCases = [ + defaultDomain, + ...['hello', 'hello-world', '11111111111', '--wow--', 'wow--', '--wow'].map(name => ({ + ...defaultDomain, + name, + })), + { + ...defaultDomain, + description: getstroflen(99), + }, + { ...defaultDomain, record: { CNAME: 'aa.sd', URL: '121,3213' } }, + ]; + + it('should return false for invalid data', () => { + invalidCases.forEach(data => { + const { valid, errors } = validateDomainData(data); + expect(valid).toBe(false); + expect(errors.length).toBeGreaterThan(0); + }); + }); + + it('should return true if the name is valid', () => { + validCases.forEach(data => { + const { valid, errors } = validateDomainData(data); + if (!valid) console.log(errors); + expect(valid).toBe(true); + expect(errors).toEqual([]); + }); + }); +}); + diff --git a/utils/domain.js b/utils/domain.js index 583fbfd56..e932aafff 100644 --- a/utils/domain.js +++ b/utils/domain.js @@ -1,13 +1,11 @@ const fs = require('fs'); const path = require('path'); const R = require('ramda'); -const { VALID_RECORD_TYPES, DOMAINS_PATH } = require('./constants'); - -const log = m => x => console.log(m, x) || x; +const { DOMAINS_PATH } = require('./constants'); const toDomain = str => path.join(DOMAINS_PATH, str); -const toDomainData = R.compose(require, toDomain); +const toDomainData = R.compose(JSON.parse, R.toString, fs.readFileSync, toDomain); const getDomains = () => fs.promises.readdir(DOMAINS_PATH, {}) @@ -16,67 +14,4 @@ const getDomains = () => name: name.replace(/\.json$/, ''), }))); -const between = (min, max) => num => num >= min && num <= max; -const testRegex = regex => str => !!(str && str.match(regex)); - -const validate = pattern => data => R.compose( - invalidPairs => invalidPairs.length ? { errors: invalidPairs, valid: false } : { errors: [], valid: true }, - R.filter(([key, { fn }]) => fn ? !fn(data[key]) : false), - R.toPairs, -)(pattern); - -const or = R.anyPass; -const and = R.allPass; - -const validateCnameRecord = key => and([ - R.propSatisfies(R.is(String), key), - R.compose(R.equals(1), R.length, R.reject(R.equals('URL')), R.keys), - R.propSatisfies(R.compose(R.gte(R.__, 3), R.length), key), - R.propSatisfies(R.complement(testRegex(/^https?:\/\//ig)), key), -]); - -const validateARecord = key => and([ - R.compose(R.equals(1), R.length, R.keys), - R.propSatisfies(R.compose(R.gte(R.__, 1), R.length), key), -]); - -const validateDomainData = validate({ - name: { - reason: 'The name of the file is invalid', - fn: or([ - R.equals('@'), - and([ - R.compose(between(2, 100), R.length), - testRegex(/^[a-z0-9\-]+$/g), - ]) - ]), - }, - description: { reason: '', fn: R.T, }, - repo: { reason: '', fn: R.T, }, - owner: { - reason: '`owner` needs username and email properties', - fn: and([ - R.is(Object), - R.complement(R.isEmpty), - R.where({ - username: R.is(String), - email: R.is(String), - }), - ]), - }, - record: { - reason: 'Invalid record. CNAME records have to be a host name and A records has to be a list of ips', - fn: and([ - R.is(Object), - R.compose(R.isEmpty, R.flip(R.difference)(VALID_RECORD_TYPES), R.keys), - R.cond([ - [R.has('CNAME'), validateCnameRecord('CNAME')], - [R.has('A'), validateARecord('A')], - [R.has('URL'), R.propSatisfies(R.is(String), 'URL')], - [R.T, R.T], - ]), - ]), - }, -}); - -module.exports = { getDomains, validateDomainData }; +module.exports = { getDomains }; diff --git a/utils/helpers.js b/utils/helpers.js new file mode 100644 index 000000000..713a06797 --- /dev/null +++ b/utils/helpers.js @@ -0,0 +1,20 @@ +const R = require('ramda'); + +const log = m => x => console.log(m, x) || x; + +const between = (min, max) => num => num >= min && num <= max; +const testRegex = regex => str => !!(str && str.match(regex)); + +const validate = pattern => data => R.compose( + invalidPairs => invalidPairs.length ? { errors: invalidPairs, valid: false } : { errors: [], valid: true }, + R.filter(([key, { fn }]) => fn ? !fn(data[key]) : false), + R.toPairs, +)(pattern); + +const or = R.anyPass; +const and = R.allPass; + +const then = fn => p => p.then(fn); + +module.exports = { or, and, validate, between, testRegex, log, then }; + diff --git a/utils/validations.js b/utils/validations.js new file mode 100644 index 000000000..cd5acdb8e --- /dev/null +++ b/utils/validations.js @@ -0,0 +1,56 @@ +const R = require('ramda'); +const { VALID_RECORD_TYPES } = require('./constants'); +const { or, and, validate, between, testRegex } = require('./helpers'); + +const validateCnameRecord = key => and([ + R.propSatisfies(R.is(String), key), + R.compose(R.equals(1), R.length, R.reject(R.equals('URL')), R.keys), + R.propSatisfies(R.compose(R.gte(R.__, 3), R.length), key), + R.propSatisfies(R.complement(testRegex(/^https?:\/\//ig)), key), +]); + +const validateARecord = key => and([ + R.compose(R.equals(1), R.length, R.keys), + R.propSatisfies(R.compose(R.gte(R.__, 1), R.length), key), +]); + +const validateDomainData = validate({ + name: { + reason: 'The name of the file is invalid', + fn: or([ + R.equals('@'), + and([ + R.compose(between(2, 100), R.length), + testRegex(/^[a-z0-9\-]+$/g), + ]) + ]), + }, + description: { reason: '', fn: R.T, }, + repo: { reason: '', fn: R.T, }, + owner: { + reason: '`owner` needs username and email properties', + fn: and([ + R.is(Object), + R.complement(R.isEmpty), + R.where({ + username: R.is(String), + email: R.is(String), + }), + ]), + }, + record: { + reason: 'Invalid record. CNAME records have to be a host name and A records has to be a list of ips', + fn: and([ + R.is(Object), + R.compose(R.isEmpty, R.flip(R.difference)(VALID_RECORD_TYPES), R.keys), + R.cond([ + [R.has('CNAME'), validateCnameRecord('CNAME')], + [R.has('A'), validateARecord('A')], + [R.has('URL'), R.propSatisfies(R.is(String), 'URL')], + [R.T, R.T], + ]), + ]), + }, +}); + +module.exports = { validateDomainData };