Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Validate json for importing and exporting TTL data #946

Closed
wants to merge 13 commits into from
197 changes: 197 additions & 0 deletions __tests__/__main__/validate-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/* eslint-disable no-undef */
'use strict';

const {
validateJSON
} = require('../../js/validate-json');
thamara marked this conversation as resolved.
Show resolved Hide resolved

describe('Validate json', function()
{
process.env.NODE_ENV = 'test';
describe('validateJSON(instance)', function()
{
describe('validate type', function()
{
const validFlexibleType = [{ 'type': 'flexible', 'date': '2020-06-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const validWaivedType = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '08:00' }];
const invalidTypeValue = [{ 'type': 'not valid type', 'date': '2020-06-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const invalidTypeType = [{ 'type': ['not valid type'], 'date': '2020-06-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];

test('should be valid JSON', () =>
{
expect(validateJSON(validWaivedType)).toBeTruthy();
expect(validateJSON(validFlexibleType)).toBeTruthy();
});
test('should not be valid JSON', () =>
{
expect(validateJSON(invalidTypeValue)).not.toBeTruthy();
expect(validateJSON(invalidTypeType)).not.toBeTruthy();
});
});

thamara marked this conversation as resolved.
Show resolved Hide resolved
thamara marked this conversation as resolved.
Show resolved Hide resolved
describe('validate date with and without leading 0', function()
{
const validFlexibleDate1 = [{ 'type': 'flexible', 'date': '2020-06-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const validFlexibleDate2 = [{ 'type': 'flexible', 'date': '2020-6-3', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const validWaivedDate1 = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '08:00' }];
const validWaivedDate2 = [{ 'type': 'waived', 'date': '2020-6-3', 'data': 'waived', 'hours': '08:00' }];
test('should be valid JSON', () =>
{
expect(validateJSON(validFlexibleDate1)).toBeTruthy();
expect(validateJSON(validFlexibleDate2)).toBeTruthy();
expect(validateJSON(validWaivedDate1)).toBeTruthy();
expect(validateJSON(validWaivedDate2)).toBeTruthy();
});
});

describe('validate date', function()
{
const validFlexibleDate = [{ 'type': 'flexible', 'date': '2020-06-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const validWaivedDate = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '08:00' }];
const invalidDateFormat = [{ 'type': 'flexible', 'date': '03-06-2020', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const invalidDateType = [{ 'type': 'flexible', 'date': ['2020-06-13'], 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const invalidDateValue = [{ 'type': 'flexible', 'date': '2020-26-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const invalidDayInMonth = [{ 'type': 'flexible', 'date': '2020-04-31', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
test('should be valid JSON', () =>
{
expect(validateJSON(validWaivedDate)).toBeTruthy();
expect(validateJSON(validFlexibleDate)).toBeTruthy();
});
test('should not be valid JSON', () =>
{
expect(validateJSON(invalidDateFormat)).not.toBeTruthy();
expect(validateJSON(invalidDateType)).not.toBeTruthy();
expect(validateJSON(invalidDateValue)).not.toBeTruthy();
expect(validateJSON(invalidDayInMonth)).not.toBeTruthy();
});
});

describe('validate data', function()
{
const validData = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '08:00' }];
const invalidDataType = [{ 'type': 'waived', 'date': '2020-06-03', 'data': ['waived'], 'hours': '08:00' }];
test('should be valid JSON', () =>
{
expect(validateJSON(validData)).toBeTruthy();
});
test('should not be valid JSON', () =>
{
expect(validateJSON(invalidDataType)).not.toBeTruthy();
});
});

describe('validate hours', function()
{
const validHours = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '08:00' }];
const validHours2 = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '--:--' }];
const invalidHoursFormat = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '08-00' }];
const invalidHoursType = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': 8 }];
const invalidHoursValue = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '30:00' }];
const invalidHoursValueNegative = [{ 'type': 'waived', 'date': '2020-06-03', 'data': 'waived', 'hours': '-01:00' }];
test('should be valid JSON', () =>
{
expect(validateJSON(validHours)).toBeTruthy();
expect(validateJSON(validHours2)).toBeTruthy();
});
test('should not be valid JSON', () =>
{
expect(validateJSON(invalidHoursFormat)).not.toBeTruthy();
expect(validateJSON(invalidHoursType)).not.toBeTruthy();
expect(validateJSON(invalidHoursValue)).not.toBeTruthy();
expect(validateJSON(invalidHoursValueNegative)).not.toBeTruthy();
});
});

describe('validate values', function()
{
const validValues = [{ 'type': 'flexible', 'date': '2020-06-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const invalidValuesFormat1 = [{ 'type': 'flexible', 'date': '03-06-2020', 'values': ['0800', '12:00', '13:00', '14:00'] }];
const invalidValuesFormat2 = [{ 'type': 'flexible', 'date': '03-06-2020', 'values': ['08', '12:00', '13:00', '14:00'] }];
const invalidValuesFormat3 = [{ 'type': 'flexible', 'date': '03-06-2020', 'values': [8, '12:00', '13:00', '14:00'] }];
const invalidValuesFormat4 = [{ 'type': 'flexible', 'date': '03-06-2020', 'values': ['08-00', '12:00', '13:00', '14:00'] }];
const invalidValuesType = [{ 'type': 'flexible', 'date': ['2020-06-03'], 'values': '08:00' }];
const invalidValuesValue = [{ 'type': 'flexible', 'date': '2020-26-03', 'values': ['80:00', '12:00', '13:00', '14:00'] }];
const invalidPointsInTime = [{ 'type': 'flexible', 'date': '2020-02-01', 'values': ['08:00', '07:00', '13:00', '14:00'] }];
test('should be valid JSON', () =>
{
expect(validateJSON(validValues)).toBeTruthy();
});
test('should not be valid JSON', () =>
{
expect(validateJSON(invalidValuesFormat1)).not.toBeTruthy();
expect(validateJSON(invalidValuesFormat2)).not.toBeTruthy();
expect(validateJSON(invalidValuesFormat3)).not.toBeTruthy();
expect(validateJSON(invalidValuesFormat4)).not.toBeTruthy();
expect(validateJSON(invalidValuesType)).not.toBeTruthy();
expect(validateJSON(invalidValuesValue)).not.toBeTruthy();
expect(validateJSON(invalidPointsInTime)).not.toBeTruthy();
});
});

describe('validate every day', function()
{
const invalidDay = [{ 'type': 'flexible', 'date': '2020-12-00', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const invalidDay2 = [{ 'type': 'flexible', 'date': '2020-12-32', 'values': ['08:00', '12:00', '13:00', '14:00'] }];

test('should be valid JSON', () =>
{
for (let i = 1; i <= 9; i++)
{
const firstNineDays = [{ 'type': 'flexible', 'date': `2020-12-0${i}`, 'values': ['08:00', '12:00', '13:00', '14:00'] }];
expect(validateJSON(firstNineDays)).toBeTruthy();
}
for (let i = 10; i <= 31; i++)
{
const restDays = [{ 'type': 'flexible', 'date': `2020-12-${i}`, 'values': ['08:00', '12:00', '13:00', '14:00'] }];
expect(validateJSON(restDays)).toBeTruthy();
}
});
test('should not be valid JSON', () =>
{
expect(validateJSON(invalidDay)).not.toBeTruthy();
expect(validateJSON(invalidDay2)).not.toBeTruthy();
});
});

describe('validate every month', function()
{
const invalidMonth = [{ 'type': 'flexible', 'date': '2020-00-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const invalidMonth2 = [{ 'type': 'flexible', 'date': '2020-13-03', 'values': ['08:00', '12:00', '13:00', '14:00'] }];

test('should be valid JSON', () =>
{
for (let i = 1; i <= 9; i++)
{
const firstNineMonths = [{ 'type': 'flexible', 'date': `2020-0${i}-13`, 'values': ['08:00', '12:00', '13:00', '14:00'] }];
expect(validateJSON(firstNineMonths)).toBeTruthy();
}
for (let i = 10; i <= 12; i++)
{
const restMonths = [{ 'type': 'flexible', 'date': `2020-${i}-13`, 'values': ['08:00', '12:00', '13:00', '14:00'] }];
expect(validateJSON(restMonths)).toBeTruthy();
}
});
test('should not be valid JSON', () =>
{
expect(validateJSON(invalidMonth)).not.toBeTruthy();
expect(validateJSON(invalidMonth2)).not.toBeTruthy();
});
});

describe('validate leap year', function()
{
const validLeapYear = [{ 'type': 'flexible', 'date': '2020-02-29', 'values': ['08:00', '12:00', '13:00', '14:00'] }];
const invalidLeapYear = [{ 'type': 'flexible', 'date': '2021-02-29', 'values': ['08:00', '12:00', '13:00', '14:00'] }];

test('should be valid JSON', () =>
{
expect(validateJSON(validLeapYear)).toBeTruthy();

});
test('should not be valid JSON', () =>
{
expect(validateJSON(invalidLeapYear)).not.toBeTruthy();
});
});
});
});
173 changes: 173 additions & 0 deletions js/validate-json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
'use strict';
const Validator = require('jsonschema').Validator;
thamara marked this conversation as resolved.
Show resolved Hide resolved

const schema = {
'id': '/singleEntry',
'type': 'array',
'items': {
oneOf: [
{'$ref': '/waivedEntry'},
{'$ref': '/flexibleEntry'}
]
}
};

const schemaWaivedEntry = {
'id': '/waivedEntry',
'type': 'object',
'properties': {
'type':
{'type': 'string', 'pattern': 'waived' }
,
'date': {
'type': 'string',
'format': 'dateFormat',
'pattern': /(1|2)[0-9]{3}-(0?[1-9]{1}|1[0-2]{1})-(0?[0-9]{1}|1[0-9]{1}|2[0-9]{1}|3[0-1]{1})/
},
'data': {
'type': 'string'
},
'hours': {
'type': 'string',
'pattern': /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]|--:--$/
}
},
'required': [
'type',
'date',
'data',
'hours'
]
};

const schemaFlexibleEntry = {
'id': '/flexibleEntry',
'type': 'object',
'properties': {
'type':
{'type': 'string', 'pattern': 'flexible' }
,
'date': {
'type': 'string',
'format': 'dateFormat',
'pattern': /(1|2)[0-9]{3}-(0?[1-9]{1}|1[0-2]{1})-(0?[0-9]{1}|1[0-9]{1}|2[0-9]{1}|3[0-1]{1})/
},
'values': {
'type': 'array',
'format': 'timePointFormat',
'items': {
'type': 'string',
'pattern': /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]$/
}
}
},
'required': [
'type',
'date',
'values'
]
};

/**
* Returns the number of days in the month of a specific year.
*
* @param {number} month
* @param {number} year
*
* @return {number} number of days in month.
*/
function daysInMonth(month, year)
{
switch (month)
{
case 1 :
return (year % 4 === 0 && year % 100) || year % 400 === 0 ? 29 : 28;
case 8 : case 3 : case 5 : case 10 :
return 30;
default :
return 31;
}
}

/**
* Checks if date is valid.
* Months are counted from 0.
*
* @param {number} year
* @param {number} month
* @param {number} day
*
* @return {boolean} true or false depending on if date is valid.
*/
function isValidDate(year, month, day)
{
return month >= 0 && month < 12 && day > 0 && day <= daysInMonth(month, year);
}

/**
* Adds custom format to validator that checks if date is valid.
*
* @param {String} dateStr
*
* @return {boolean} true or false depending on if date is valid.
*/
Validator.prototype.customFormats.dateFormat = function(dateStr)
{
if (!typeof(dateStr) === 'String' || !dateStr.includes('-'))
{
return false;
}
const dateArray = dateStr.split('-');
const year = dateArray[0];
const month = dateArray[1]-1; // isValidDate(..) counts months from 0
const day = dateArray[2];

return isValidDate(year, month, day);
};

/**
* Adds custom format to validator that checks if values in flexible entry are valid.
* Items in timePointArray have to be in an ascending order.
*
* @param {Array} timePointArray
*
* @return {boolean} true or false depending on if timePointArray is valid.
*/
Validator.prototype.customFormats.timePointFormat = function(timePointArray)
{
if (!Array.isArray(timePointArray))
{
return false;
}
let isValidTimePointArray = true;
let timePointBefore = 0;
timePointArray.forEach(function(timePoint)
{
if (timePointBefore > parseInt(timePoint))
{
isValidTimePointArray = false;
}
timePointBefore = parseInt(timePoint);
});
return isValidTimePointArray;
};

/**
* Validate JSON to find out if it's in the correct format for TTl.
*
* @param {object} instance JSON instance that should be validated.
*
* @return {boolean} true or false depending on if JSON is valid TTL JSON.
*/
function validateJSON(instance)
{
const v = new Validator();
v.addSchema(schemaFlexibleEntry, '/flexibleEntry');
v.addSchema(schemaWaivedEntry, '/waivedEntry');

return v.validate(instance, schema).valid;
}

export {
validateJSON
};
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
"i18next-node-fs-backend": "^2.1.3",
"is-online": "^9.0.1",
"jquery": "^3.6.0",
"jquery-mousewheel": "^3.1.13"
"jquery-mousewheel": "^3.1.13",
"jsonschema": "^1.4.1"
thamara marked this conversation as resolved.
Show resolved Hide resolved
},
"engines": {
"node": ">=14.15.5",
Expand Down
Loading