@openactive/data-model-validator
Version:
A library to allow a developer to validate a JSON document against the OpenActive Modelling Opportunity Specification
138 lines (130 loc) • 6.82 kB
JavaScript
const DataModelHelper = require('../../helpers/data-model');
const Rule = require('../rule');
const Field = require('../../classes/field');
const ValidationErrorType = require('../../errors/validation-error-type');
const ValidationErrorCategory = require('../../errors/validation-error-category');
const ValidationErrorSeverity = require('../../errors/validation-error-severity');
module.exports = class ContextInRootNodeRule extends Rule {
constructor(options) {
super(options);
const metaData = DataModelHelper.getMetaData(this.options.version);
this.targetModels = '*';
this.meta = {
name: 'ContextInRootNodeRule',
description: 'Validates that @context is present in the root node, and that it is the correct format, containing the OA namespace.',
tests: {
noContext: {
description: 'Raises a failure if the @context is missing from the root node.',
message: `The \`\` property is required in the root object. It must contain the OpenActive context ("${metaData.contextUrl}") as a string or the first element in an array.\n\nFor example:\n\n\`\`\`\n{\n "@context": "${metaData.contextUrl}",\n "@type": "Event"\n}\n\`\`\``,
category: ValidationErrorCategory.CONFORMANCE,
severity: ValidationErrorSeverity.FAILURE,
type: ValidationErrorType.MISSING_REQUIRED_FIELD,
},
hasContext: {
description: 'Raises a failure if the @context is present in a non-root node.',
message: 'The `@context` property must only be present in the root object. Please ensure that this property is not present in other objects.',
category: ValidationErrorCategory.CONFORMANCE,
severity: ValidationErrorSeverity.FAILURE,
type: ValidationErrorType.FIELD_NOT_IN_SPEC,
},
oaNotInRightPlace: {
description: `Validates that the contains the OpenActive context (${metaData.contextUrl}) as a string or the first element in an array.`,
message: `The \`\` property must contain the OpenActive context (\`"${metaData.contextUrl}"\`) as a string or the first element in an array.\n\nFor example:\n\n\`\`\`\n{\n "@context": "${metaData.contextUrl}",\n "@type": "Event"\n}\n\`\`\``,
category: ValidationErrorCategory.CONFORMANCE,
severity: ValidationErrorSeverity.FAILURE,
type: ValidationErrorType.FIELD_NOT_IN_DEFINED_VALUES,
},
contextIncorrectForDatasetSite: {
description: `For a Dataset Site, validates that the contains the schema.org context (${metaData.namespaces.schema}) and OpenActive context (${metaData.contextUrl}) as the first and second elements in an array, respectively.`,
message: `For a Dataset Site, the \`\` property must be present in the root object and contain the schema.org context (\`"${metaData.namespaces.schema}"\`) and OpenActive context (\`"${metaData.contextUrl}"\`) as the first and second elements in an array, respectively.\n\nFor example:\n\n\`\`\`\n{\n "@context": [\n "${metaData.namespaces.schema}",\n "${metaData.contextUrl}"\n ],\n "@type": "Dataset"\n}\n\`\`\``,
category: ValidationErrorCategory.CONFORMANCE,
severity: ValidationErrorSeverity.FAILURE,
type: ValidationErrorType.FIELD_NOT_IN_DEFINED_VALUES,
},
contextIncorrectForDataCatalog: {
description: `For a DataCatalog, validates that the contains only the schema.org context (${metaData.namespaces.schema}) as a string.`,
message: `For a \`DataCatalog\`, the \`\` property must be present in the root object and must contain the schema.org context (\`"${metaData.namespaces.schema}"\`) as a string.\n\nFor example:\n\n\`\`\`\n{\n "@context": "${metaData.namespaces.schema}",\n "@type": "DataCatalog"\n}\n\`\`\``,
category: ValidationErrorCategory.CONFORMANCE,
severity: ValidationErrorSeverity.FAILURE,
type: ValidationErrorType.FIELD_NOT_IN_DEFINED_VALUES,
},
type: {
description: 'Validates that the context is a url or an array or urls.',
message: `Whilst JSON-LD supports inline context objects, for use in OpenActive the \`\` property must contain a URL or array of URLs, with each URL pointing to a published context.\n\nThe \`\` property must also contain the OpenActive context (\`"${metaData.contextUrl}"\`) as the first element in this array.`,
category: ValidationErrorCategory.CONFORMANCE,
severity: ValidationErrorSeverity.FAILURE,
type: ValidationErrorType.INVALID_TYPE,
},
},
};
}
validateModel(node) {
if (!node.model.isJsonLd) {
return [];
}
const errors = [];
const fieldObj = node.model.getField('@context');
const fieldValue = node.getValue('@context');
const metaData = DataModelHelper.getMetaData(this.options.version);
let testKey;
// Is this the root node?
if (node.parentNode === null || !node.parentNode.model.isJsonLd) {
const backupField = new Field({
requiredType: 'https://schema.org/URL',
alternativeTypes: [
'ArrayOf#https://schema.org/URL',
],
}, node.options.version);
if (node.options.validationMode === 'DatasetSite') {
if (
typeof fieldValue !== 'object'
|| !(fieldValue instanceof Array)
|| fieldValue[0] !== metaData.namespaces.schema
|| fieldValue[1] !== metaData.contextUrl
) {
testKey = 'contextIncorrectForDatasetSite';
}
} else if (node.options.validationMode === 'DataCatalog') {
if (
typeof fieldValue !== 'string'
|| fieldValue !== metaData.namespaces.schema
) {
testKey = 'contextIncorrectForDataCatalog';
}
} else if (typeof fieldValue === 'undefined') {
testKey = 'noContext';
} else if (
(typeof fieldObj === 'undefined' || fieldObj === null)
&& !backupField.detectedTypeIsAllowed(fieldValue)
) {
testKey = 'type';
} else if (
(
typeof fieldValue === 'string'
&& fieldValue !== metaData.contextUrl
)
|| (
typeof fieldValue === 'object'
&& fieldValue instanceof Array
&& fieldValue[0] !== metaData.contextUrl
)
) {
testKey = 'oaNotInRightPlace';
}
} else if (typeof fieldValue !== 'undefined') {
testKey = 'hasContext';
}
if (testKey) {
errors.push(
this.createError(
testKey,
{
value: fieldValue,
path: node.getPath('@context'),
},
),
);
}
return errors;
}
};