UNPKG

@openactive/rpde-validator

Version:

A library to walk and validate an RPDE feed

119 lines (115 loc) 4.41 kB
const { ValidationErrorCategory, ValidationErrorSeverity, } = require('@openactive/data-model-validator'); const _ = require('lodash'); const RpdeRule = require('../../rpde-rule'); const RpdeErrorType = require('../../errors/rpde-error-type'); const UrlHelper = require('../../helpers/url-helper'); class AfterChangeNumberRule extends RpdeRule { constructor() { super(); this.lastChangeNumber = null; this.meta = { name: 'AfterChangeNumberRule', description: 'If afterChangeNumber is used it must be an integer and "modified" must be an integer', tests: { default: { description: 'Raises a failure if "afterChangeNumber" isn\'t an integer', message: 'When using the [Incrementing Unique Change Number Ordering Strategy](https://www.openactive.io/realtime-paged-data-exchange/#incrementing-unique-change-number), the parameter `afterChangeNumber` must be an integer.', category: ValidationErrorCategory.CONFORMANCE, severity: ValidationErrorSeverity.FAILURE, type: RpdeErrorType.INVALID_TYPE, }, modified: { description: 'Raises a failure if "afterChangeNumber" is used and "modified" isn\'t an integer', message: 'When using the [Incrementing Unique Change Number Ordering Strategy](https://www.openactive.io/realtime-paged-data-exchange/#incrementing-unique-change-number), `modified` must be an integer.', category: ValidationErrorCategory.CONFORMANCE, severity: ValidationErrorSeverity.FAILURE, type: RpdeErrorType.INVALID_TYPE, }, increment: { description: 'Raises a failure if the afterChangeNumber doesn\'t increase with each new page', message: 'When using the [Incrementing Unique Change Number Ordering Strategy](https://www.openactive.io/realtime-paged-data-exchange/#incrementing-unique-change-number), the `afterChangeNumber` in the next url must always increase with each new page, as the primary query sorts by the change number (the `modified` property). The next URL of this page is "{{url}}".', sampleValues: { url: 'https://example.org/feed', }, category: ValidationErrorCategory.CONFORMANCE, severity: ValidationErrorSeverity.FAILURE, type: RpdeErrorType.AFTER_PARAM_SHOULD_INCREASE, }, }, }; } validate(node) { if ( typeof node.data !== 'object' || node.isLastPage ) { return; } const lastChangeNumber = UrlHelper.getParam('afterChangeNumber', node.url) || this.lastChangeNumber; const afterChangeNumber = UrlHelper.getParam('afterChangeNumber', node.data.next, node.url); if (afterChangeNumber !== null) { const modified = _.get(node.data, 'items[0].modified'); if ( _.isNil(modified) || (typeof modified !== 'number' && !modified.match(/^[1-9][0-9]*$/) ) ) { node.log.addPageError( node.url, this.createError( 'modified', { value: node.data, url: node.url, }, ), ); } if (!afterChangeNumber.match(/^[1-9][0-9]*$/)) { node.log.addPageError( node.url, this.createError( 'default', { value: node.data, url: node.url, }, ), ); } let compareAfterChangeNumber = afterChangeNumber; let compareLastChangeNumber = lastChangeNumber; if ( typeof compareAfterChangeNumber === 'string' && compareAfterChangeNumber.match(/^[1-9][0-9]*$/) ) { compareAfterChangeNumber *= 1; } if ( typeof compareLastChangeNumber === 'string' && compareLastChangeNumber.match(/^[1-9][0-9]*$/) ) { compareLastChangeNumber *= 1; } if (!node.isLastPage && node.pageIndex > 0 && compareAfterChangeNumber <= compareLastChangeNumber) { node.log.addPageError( node.url, this.createError( 'increment', { value: node.data, url: node.url, }, { url: node.data.next, }, ), ); } this.lastChangeNumber = afterChangeNumber; } } } module.exports = AfterChangeNumberRule;