-
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.
feat: Add discussionUrl rules checking against GH issues (#445)
- Loading branch information
1 parent
ec840d6
commit adbf497
Showing
5 changed files
with
164 additions
and
2 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
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,27 @@ | ||
/** | ||
* A shorthand to create an object where keys and values are the same. | ||
* | ||
* e.g. | ||
* | ||
* ```js | ||
* > SelfIndexingObject.create(['a', 'b', 'c']) | ||
* { a: 'a', b: 'b', c: 'c' } | ||
* ``` | ||
*/ | ||
const SelfIndexingObject = { | ||
/** | ||
* @template TKey | ||
* @param {TKey[]} keys | ||
* @returns {Record<TKey, TKey>} | ||
*/ | ||
create(keys) { | ||
return keys.reduce((acc, key) => { | ||
acc[key] = key; | ||
return acc; | ||
}, /** @type {any} */({})); | ||
}, | ||
}; | ||
|
||
module.exports = { | ||
SelfIndexingObject, | ||
}; |
54 changes: 54 additions & 0 deletions
54
...ules/data-quality/discussion-url-should-point-to-recognised-discussion-board-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,54 @@ | ||
const DiscussionUrlShouldPointToRecognisedDiscussionBoardRule = require('./discussion-url-should-point-to-recognised-discussion-board-rule'); | ||
const ModelNode = require('../../classes/model-node'); | ||
const Model = require('../../classes/model'); | ||
const DataModelHelper = require('../../helpers/data-model'); | ||
const ValidationErrorType = require('../../errors/validation-error-type'); | ||
const ValidationErrorCategory = require('../../errors/validation-error-category'); | ||
const ValidationErrorSeverity = require('../../errors/validation-error-severity'); | ||
|
||
describe('DiscussionUrlShouldPointToRecognisedDiscussionBoardRule', () => { | ||
const rule = new DiscussionUrlShouldPointToRecognisedDiscussionBoardRule(); | ||
|
||
const model = new Model( | ||
DataModelHelper.loadModel('Dataset', 'latest'), | ||
'latest', | ||
true, | ||
); | ||
|
||
it('should target Dataset discussionUrl', () => { | ||
const isTargeted = rule.isFieldTargeted(model, 'discussionUrl'); | ||
expect(isTargeted).toBe(true); | ||
}); | ||
|
||
it('should return no error when discussionUrl points to a GitHub repo\'s /issues', async () => { | ||
const nodeToTest = new ModelNode('$', { | ||
discussionUrl: 'https://github.com/openactive/openactive-test-suite/issues', | ||
}, null, model); | ||
const errors = await rule.validate(nodeToTest); | ||
expect(errors).toHaveSize(0); | ||
}); | ||
|
||
it('should return an error when discussionUrl points to a GitHub repo but not to /issues', async () => { | ||
const nodeToTest = new ModelNode('$', { | ||
discussionUrl: 'https://github.com/openactive/openactive-test-suite', | ||
}, null, model); | ||
const errors = await rule.validate(nodeToTest); | ||
expect(errors).toHaveSize(1); | ||
expect(errors[0].rule).toEqual('DiscussionUrlShouldPointToRecognisedDiscussionBoardRule'); | ||
expect(errors[0].category).toEqual(ValidationErrorCategory.DATA_QUALITY); | ||
expect(errors[0].type).toEqual(ValidationErrorType.INVALID_FORMAT); | ||
expect(errors[0].severity).toEqual(ValidationErrorSeverity.FAILURE); | ||
}); | ||
|
||
it('should return a warning when discussionUrl points to an unrecognised place', async () => { | ||
const nodeToTest = new ModelNode('$', { | ||
discussionUrl: 'https://example.com/some-place', | ||
}, null, model); | ||
const errors = await rule.validate(nodeToTest); | ||
expect(errors).toHaveSize(1); | ||
expect(errors[0].rule).toEqual('DiscussionUrlShouldPointToRecognisedDiscussionBoardRule'); | ||
expect(errors[0].category).toEqual(ValidationErrorCategory.CONFORMANCE); | ||
expect(errors[0].type).toEqual(ValidationErrorType.INVALID_FORMAT); | ||
expect(errors[0].severity).toEqual(ValidationErrorSeverity.WARNING); | ||
}); | ||
}); |
79 changes: 79 additions & 0 deletions
79
src/rules/data-quality/discussion-url-should-point-to-recognised-discussion-board-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,79 @@ | ||
const Rule = require('../rule'); | ||
const ValidationErrorType = require('../../errors/validation-error-type'); | ||
const ValidationErrorCategory = require('../../errors/validation-error-category'); | ||
const ValidationErrorSeverity = require('../../errors/validation-error-severity'); | ||
const { SelfIndexingObject } = require('../../helpers/self-indexing-object'); | ||
|
||
const TEST_KEYS = SelfIndexingObject.create(/** @type {const} */([ | ||
'githubButNotIssues', | ||
'unrecognisedFormat', | ||
])); | ||
|
||
module.exports = class DiscussionUrlShouldPointToRecognisedDiscussionBoardRule extends Rule { | ||
constructor(options) { | ||
super(options); | ||
this.targetFields = { | ||
Dataset: ['discussionUrl'], | ||
}; | ||
this.meta = { | ||
name: 'DiscussionUrlShouldPointToRecognisedDiscussionBoardRule', | ||
description: 'Validates that the `discussionUrl` property points to a recognised discussion board.', | ||
tests: { | ||
[TEST_KEYS.githubButNotIssues]: { | ||
message: 'If `discussionUrl` points to GitHub, it should be to the project\'s /issues page e.g. `https://github.com/openactive/OpenActive.Server.NET/issues`.', | ||
category: ValidationErrorCategory.DATA_QUALITY, | ||
severity: ValidationErrorSeverity.FAILURE, | ||
type: ValidationErrorType.INVALID_FORMAT, | ||
}, | ||
[TEST_KEYS.unrecognisedFormat]: { | ||
message: 'The `discussionUrl` property does not point to a recognised discussion board. Currently recognised discussion board formats: `https://github.com/<ORG>/<REPO>/issues`.', | ||
category: ValidationErrorCategory.CONFORMANCE, | ||
severity: ValidationErrorSeverity.WARNING, | ||
type: ValidationErrorType.INVALID_FORMAT, | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
/** | ||
* @param {import('../../classes/model-node').ModelNodeType} node | ||
* @param {string} field | ||
*/ | ||
async validateField(node, field) { | ||
const discussionUrlRaw = node.getValue(field); | ||
const discussionUrl = new URL(discussionUrlRaw); | ||
if (discussionUrl.hostname === 'github.com') { | ||
return this.validateGitHubUrl(discussionUrl, node, field); | ||
} | ||
return [ | ||
this.createError( | ||
TEST_KEYS.unrecognisedFormat, | ||
{ | ||
value: node.getValue(field), | ||
path: node.getPath(field), | ||
}, | ||
), | ||
]; | ||
} | ||
|
||
/** | ||
* @param {URL} discussionUrl | ||
* @param {import('../../classes/model-node').ModelNodeType} node | ||
* @param {string} field | ||
*/ | ||
validateGitHubUrl(discussionUrl, node, field) { | ||
const isGhIssuesUrl = discussionUrl.pathname.endsWith('/issues'); | ||
if (isGhIssuesUrl) { | ||
return []; | ||
} | ||
return [ | ||
this.createError( | ||
TEST_KEYS.githubButNotIssues, | ||
{ | ||
value: node.getValue(field), | ||
path: node.getPath(field), | ||
}, | ||
), | ||
]; | ||
} | ||
}; |
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