-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
data-quality: Ensure Schedule templates (id & url) are valid UriTempl…
…ates
- Loading branch information
Jared Parnell
committed
Mar 24, 2021
1 parent
00faf5f
commit 8d48385
Showing
3 changed files
with
183 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
127 changes: 127 additions & 0 deletions
127
src/rules/data-quality/schedule-templates-are-valid-rule-spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
const ScheduleTemplatesValid = require('./schedule-templates-are-valid-rule'); | ||
const Model = require('../../classes/model'); | ||
const ModelNode = require('../../classes/model-node'); | ||
const ValidationErrorType = require('../../errors/validation-error-type'); | ||
const ValidationErrorSeverity = require('../../errors/validation-error-severity'); | ||
|
||
describe('ScheduleTemplatesValid', () => { | ||
let model; | ||
let rule; | ||
|
||
beforeEach(() => { | ||
model = new Model({ | ||
type: 'Schedule', | ||
fields: { | ||
idTemplate: { | ||
fieldName: 'idTemplate', | ||
sameAs: 'https://openactive.io/idTemplate', | ||
requiredType: 'https://schema.org/Text', | ||
example: 'https://api.example.org/session-series/123/{startDate}', | ||
description: [ | ||
'An RFC6570 compliant URI template that can be used to generate a unique identifier (`@id`) for every event described by the schedule. This property is required if the data provider is supporting third-party booking via the Open Booking API, or providing complimentary individual `subEvent`s.', | ||
], | ||
valueConstraint: 'UriTemplate', | ||
}, | ||
urlTemplate: { | ||
fieldName: 'urlTemplate', | ||
sameAs: 'https://schema.org/urlTemplate', | ||
requiredType: 'https://schema.org/Text', | ||
example: 'https://example.org/session-series/123/{startDate}', | ||
description: [ | ||
'An RFC6570 compliant URI template that can be used to generate a unique `url` for every event described by the schedule. This property is required if the data provider wants to provide participants with a unique URL to book to attend an event.', | ||
], | ||
valueConstraint: 'UriTemplate', | ||
}, | ||
}, | ||
}, 'latest'); | ||
rule = new ScheduleTemplatesValid(); | ||
}); | ||
|
||
it('should target idTemplate and urlTemplate in Schedule model', () => { | ||
let isTargeted = rule.isFieldTargeted(model, 'idTemplate'); | ||
expect(isTargeted).toBe(true); | ||
|
||
isTargeted = rule.isFieldTargeted(model, 'urlTemplate'); | ||
expect(isTargeted).toBe(true); | ||
}); | ||
|
||
it('should return no errors if the urlTemplate is valid', async () => { | ||
const data = { | ||
'@type': 'Schedule', | ||
urlTemplate: 'https://api.example.org/session-series/123/{startDate}', | ||
}; | ||
|
||
const nodeToTest = new ModelNode( | ||
'$', | ||
data, | ||
null, | ||
model, | ||
); | ||
|
||
const errors = await rule.validate(nodeToTest); | ||
|
||
expect(errors.length).toBe(0); | ||
}); | ||
|
||
it('should return no errors if the idTemplate is valid', async () => { | ||
const data = { | ||
'@type': 'Schedule', | ||
idTemplate: 'https://api.example.org/session-series/123/{startDate}', | ||
}; | ||
|
||
const nodeToTest = new ModelNode( | ||
'$', | ||
data, | ||
null, | ||
model, | ||
); | ||
|
||
const errors = await rule.validate(nodeToTest); | ||
|
||
expect(errors.length).toBe(0); | ||
}); | ||
|
||
it('should return errors if the urlTemplate is not valid', async () => { | ||
const data = { | ||
'@type': 'Schedule', | ||
urlTemplate: 'htts://api.example.org/session-series/123/', | ||
}; | ||
|
||
const nodeToTest = new ModelNode( | ||
'$', | ||
data, | ||
null, | ||
model, | ||
); | ||
|
||
const errors = await rule.validate(nodeToTest); | ||
|
||
expect(errors.length).toBe(1); | ||
for (const error of errors) { | ||
expect(error.type).toBe(ValidationErrorType.INVALID_FORMAT); | ||
expect(error.severity).toBe(ValidationErrorSeverity.FAILURE); | ||
} | ||
}); | ||
|
||
it('should return errors if the idTemplate is not valid', async () => { | ||
const data = { | ||
'@type': 'Schedule', | ||
idTemplate: 'htts://api.example.org/session-series/123/', | ||
}; | ||
|
||
const nodeToTest = new ModelNode( | ||
'$', | ||
data, | ||
null, | ||
model, | ||
); | ||
|
||
const errors = await rule.validate(nodeToTest); | ||
|
||
expect(errors.length).toBe(1); | ||
for (const error of errors) { | ||
expect(error.type).toBe(ValidationErrorType.INVALID_FORMAT); | ||
expect(error.severity).toBe(ValidationErrorSeverity.FAILURE); | ||
} | ||
}); | ||
}); |
52 changes: 52 additions & 0 deletions
52
src/rules/data-quality/schedule-templates-are-valid-rule.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
const Rule = require('../rule'); | ||
const PropertyHelper = require('../../helpers/property'); | ||
const ValidationErrorType = require('../../errors/validation-error-type'); | ||
const ValidationErrorCategory = require('../../errors/validation-error-category'); | ||
const ValidationErrorSeverity = require('../../errors/validation-error-severity'); | ||
|
||
module.exports = class ScheduleTemplatesValid extends Rule { | ||
constructor(options) { | ||
super(options); | ||
this.targetFields = { Schedule: ['urlTemplate', 'idTemplate'] }; | ||
this.meta = { | ||
name: 'ScheduleTemplatesValid', | ||
description: 'Validates that the urlTemplate is of the correct format', | ||
tests: { | ||
default: { | ||
description: 'Validates that the @context url matches the correct scheme and subdomain (https://openactive.io).', | ||
message: 'When referencing the OpenActive domain, you must start your URLs with https://openactive.io.', | ||
category: ValidationErrorCategory.CONFORMANCE, | ||
severity: ValidationErrorSeverity.FAILURE, | ||
type: ValidationErrorType.INVALID_FORMAT, | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
validateField(node, field) { | ||
const fieldObj = node.model.getField(field); | ||
const fieldValue = node.getValue(field); | ||
|
||
if (typeof fieldValue !== 'string') { | ||
return []; | ||
} | ||
|
||
const errors = []; | ||
|
||
if (typeof fieldObj.valueConstraint !== 'undefined' | ||
&& (fieldObj.valueConstraint === 'UriTemplate' | ||
&& !PropertyHelper.isUrlTemplate(fieldValue))) { | ||
errors.push( | ||
this.createError( | ||
'default', | ||
{ | ||
fieldValue, | ||
path: node.getPath(field), | ||
}, | ||
), | ||
); | ||
} | ||
|
||
return errors; | ||
} | ||
}; |