contentful-migration
Version:
Migration tooling for contentful
377 lines • 16 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.validateTag = exports.validateFields = exports.validateEditorInterface = exports.validateContentType = void 0;
const _ = __importStar(require("lodash"));
const hoek_1 = require("@hapi/hoek");
const kind_of_1 = __importDefault(require("kind-of"));
const errors_1 = __importDefault(require("../errors"));
const content_type_schema_1 = require("./content-type-schema");
const tag_schema_1 = require("./tag-schema");
const fields_schema_1 = require("./fields-schema");
const editor_layout_schema_1 = require("./editor-layout-schema");
const validateContentType = function (contentType) {
const contentTypeId = contentType.id;
const { error } = content_type_schema_1.contentTypeSchema.validate(_.omit(contentType.toAPI(), ['sys']), {
abortEarly: false
});
if (!error) {
return [];
}
return error.details.map(({ path, type }) => {
if (type === 'any.required') {
return {
type: 'InvalidPayload',
message: errors_1.default.contentType.REQUIRED_PROPERTY(path)
};
}
if (type === 'array.max' && path[0] === 'fields' && path.length === 1) {
return {
type: 'InvalidPayload',
message: errors_1.default.contentType.TOO_MANY_FIELDS(contentTypeId, content_type_schema_1.MAX_FIELDS)
};
}
});
};
exports.validateContentType = validateContentType;
const validateEditorInterface = function (editorInterface) {
const groupControls = editorInterface.getGroupControls() || [];
const tabsIds = groupControls
.filter((control) => control.widgetNamespace === 'builtin' && control.widgetId === 'topLevelTab')
.map((control) => control.groupId);
const validateResult = (0, editor_layout_schema_1.createEditorLayoutSchema)(tabsIds).validate(editorInterface.getEditorLayout(), {
abortEarly: false
});
const { error } = validateResult;
if (!error) {
return [];
}
return error.details.map((err) => {
if (err.type === 'array.max' && err.path.length === 0) {
return {
type: 'InvalidPayload',
message: errors_1.default.editorLayout.TOO_MANY_TABS()
};
}
if (err.type === 'any.only') {
return {
type: 'InvalidPayload',
message: errors_1.default.editorLayout.TAB_CONTROL_INVALID(err.context.value)
};
}
if (err.type === 'any.invalid') {
return {
type: 'InvalidPayload',
message: errors_1.default.editorLayout.FIELD_SET_CONTROL_INVALID(err.context.value)
};
}
if (err.type === 'any.required') {
return {
type: 'InvalidPayload',
message: errors_1.default.editorLayout.FIELD_GROUP_LEVEL_TOO_DEEP()
};
}
});
};
exports.validateEditorInterface = validateEditorInterface;
const validateTag = function (tag) {
const { error } = tag_schema_1.tagSchema.validate(_.omit(tag.toApiTag(), ['sys']), {
abortEarly: false
});
if (!error) {
return [];
}
return error.details.map(({ path, type }) => {
if (type === 'any.required') {
return {
type: 'InvalidPayload',
message: errors_1.default.tag.REQUIRED_PROPERTY(path)
};
}
});
};
exports.validateTag = validateTag;
const unknownCombinationError = function ({ path, keys }) {
const type = 'object.unknownCombination';
const message = `"${keys.join()}" is not allowed`;
const context = { keys };
return { message, path, type, context };
};
// Receives ValidationError[] with this structure:
// [{
// message: '"foo" is not allowed',
// path: [0, 'validations', 2, 'foo'],
// type: 'object.allowUnknown',
// context: { child: 'foo', key: 'foo' }
// }]
const combineErrors = function (fieldValidationsError) {
const fieldValidationsErrors = fieldValidationsError.context.details;
const byItemPath = _.groupBy(fieldValidationsErrors, ({ path }) => {
return path.slice(0, -1).join('.');
});
// Remove object.unknown error for alternatives.match field validation if there are deeper nested validation errors
if (fieldValidationsError.type === 'alternatives.match' && Object.keys(byItemPath).length > 1) {
const alternativesMatchErrorPath = fieldValidationsError.path.join('.');
delete byItemPath[alternativesMatchErrorPath];
}
const pathErrorTuples = _.entries(byItemPath);
const uniqPropErrorsByPath = _.map(pathErrorTuples, ([path, itemErrors]) => {
const uniqErrors = _.uniqBy(itemErrors, 'context.key');
return [path, uniqErrors];
});
return uniqPropErrorsByPath.map(([path, errors]) => {
const keys = errors.map((error) => (0, hoek_1.reach)(error, 'context.key'));
// Invalid property
if (keys.length === 1) {
const [errorSample] = errors;
return errorSample;
}
// Invalid combined properties
return unknownCombinationError({ path: path.split('.'), keys });
});
};
// Joi might return an `object.unknown` error for each
// non-matched field validation in `Joi.alternatives.try()`.
// They are noise, execept when all error types are the same.
const cleanNoiseFromJoiErrors = function (error) {
const [normalErrors, fieldValidationsErrors] = _.partition(error.details, (detail) => {
const [, fieldProp] = detail.path;
return fieldProp !== 'validations';
});
if (!fieldValidationsErrors.length) {
return normalErrors;
}
let allErrors = [...normalErrors];
for (const fieldValidationsError of fieldValidationsErrors) {
const errorDetails = fieldValidationsError.context.details;
if (!errorDetails) {
allErrors = [...allErrors, fieldValidationsError];
continue;
}
const isUnknownValidationsProp = errorDetails.every(({ type }) => type === 'object.unknown');
if (isUnknownValidationsProp) {
const combinedErrors = combineErrors(fieldValidationsError);
allErrors = [...allErrors, ...combinedErrors];
continue;
}
const remainingFieldValidationsErrors = errorDetails.filter(({ type }) => type !== 'object.unknown');
allErrors = [...allErrors, ...remainingFieldValidationsErrors];
}
return allErrors;
};
const validateFields = function (contentType, locales) {
const fields = contentType.fields.toRaw();
const validateResult = (0, fields_schema_1.createFieldsSchema)(locales).validate(fields, {
abortEarly: false
});
const { error } = validateResult;
if (!error) {
return [];
}
const cleanErrors = cleanNoiseFromJoiErrors(error);
return cleanErrors.map((details) => {
const { path, type, context } = details;
// `path` looks like [0, 'field']
// look up the field
const [index, ...fieldNames] = path;
const prop = fieldNames.join('.');
const field = fields[index];
if (prop.startsWith('defaultValue')) {
if (type === 'object.unknown') {
return {
type: 'InvalidPayload',
message: errors_1.default.field.defaultValue.INVALID_LOCALE(field.id, context.key)
};
}
if (type === 'any.unknown') {
if (field.type === 'Array') {
return {
type: 'InvalidPayload',
message: errors_1.default.field.defaultValue.UNSUPPORTED_ARRAY_ITEMS_TYPE(field.id, path[1], field.items.type)
};
}
return {
type: 'InvalidPayload',
message: errors_1.default.field.defaultValue.UNSUPPORTED_FIELD_TYPE(field.id, path[1], field.type)
};
}
if (field.type === 'Date') {
return {
type: 'InvalidPayload',
message: errors_1.default.field.defaultValue.DATE_TYPE_MISMATCH(field.id, (0, kind_of_1.default)(context.value), context.value, context.key, field.type)
};
}
return {
type: 'InvalidPayload',
message: errors_1.default.field.defaultValue.TYPE_MISMATCH(field.id, (0, kind_of_1.default)(context.value), context.key, field.type)
};
}
// Field `allowedResources` errors
if (prop.startsWith('allowedResources')) {
if (path.length > 3) {
const error = details.message.replace(/".+?"/, `"${context.key}"`);
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.INVALID_RESOURCE_PROPERTY(field.id, path[2], error)
};
}
if (type === 'object.base') {
const actualType = (0, kind_of_1.default)((0, hoek_1.reach)(field, prop));
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.INVALID_RESOURCE(field.id, context.key, actualType)
};
}
if (type === 'array.min') {
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.TOO_FEW_ITEMS(field.id, 'allowedResources')
};
}
if (type === 'array.max') {
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.TOO_MANY_ITEMS(field.id, 'allowedResources')
};
}
if (type === 'array.unique') {
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.DUPLICATE_SOURCE(field.id, context.value.source, 'allowedResources')
};
}
}
else if (path.slice(-2).includes('allowedResources')) {
if (type === 'array.min') {
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.TOO_FEW_ITEMS(field.id, prop)
};
}
if (type === 'array.max') {
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.TOO_MANY_ITEMS(field.id, prop)
};
}
if (type === 'array.unique') {
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.DUPLICATE_SOURCE(field.id, context.value.source, prop)
};
}
}
else if (['validations', 'nodes', 'allowedResources'].every((pathItem) => path.includes(pathItem))) {
const error = details.message.replace(/".+?"/, `"${context.key}"`);
return {
type: 'InvalidPayload',
message: errors_1.default.allowedResources.INVALID_RESOURCE_PROPERTY(field.id, path[6], error)
};
}
if (type === 'any.required') {
if (context.isRequiredDependency) {
const dependentProp = context.dependsOn.key;
const dependentValue = context.dependsOn.value;
return {
type: 'InvalidPayload',
message: errors_1.default.field.REQUIRED_DEPENDENT_PROPERTY(prop, field.id, dependentProp, dependentValue)
};
}
return {
type: 'InvalidPayload',
message: errors_1.default.field.REQUIRED_PROPERTY(prop, field.id)
};
}
if (type === 'any.unknown') {
if (context.isForbiddenDependency) {
const dependentProp = context.dependsOn.key;
const dependentValue = context.dependsOn.value;
return {
type: 'InvalidPayload',
message: errors_1.default.field.FORBIDDEN_DEPENDENT_PROPERTY(prop, field.id, dependentProp, dependentValue)
};
}
return {
type: 'InvalidPayload',
message: errors_1.default.field.FORBIDDEN_PROPERTY(prop, field.id)
};
}
if (type === 'any.only') {
return {
type: 'InvalidPayload',
message: errors_1.default.field.PROPERTY_MUST_BE_ONE_OF(prop, field.id, context.valids)
};
}
if (type === 'any.invalid' && path.includes('newId')) {
return {
type: 'InvalidPayload',
message: errors_1.default.field.ID_MUST_BE_DIFFERENT(field.id)
};
}
const isIdSchemaError = (type) => {
return ['string.max', 'string.empty', 'string.pattern.base'].includes(type);
};
if (path.includes('newId') && isIdSchemaError(type)) {
return {
type: 'InvalidPayload',
message: errors_1.default.field.ID_MUST_MATCH_SCHEMA(field.id, field.newId)
};
}
// Field `validations` errors
if (prop.startsWith('validations')) {
if (type === 'array.unique') {
return {
type: 'InvalidPayload',
message: errors_1.default.field.validations.DUPLICATED_VALIDATION(context.dupeValue)
};
}
if (type === 'object.unknown') {
return {
type: 'InvalidPayload',
message: errors_1.default.field.validations.INVALID_VALIDATION_PROPERTY(context.key)
};
}
if (type === 'object.unknownCombination') {
return {
type: 'InvalidPayload',
message: errors_1.default.field.validations.INVALID_VALIDATION_PROPERTY_COMBINATION(context.keys)
};
}
if (type.endsWith('.base')) {
const [expectedType] = type.split('.base');
const actualType = (0, kind_of_1.default)((0, hoek_1.reach)(field, prop));
return {
type: 'InvalidPayload',
message: errors_1.default.field.validations.INVALID_VALIDATION_PARAMETER(context.key, expectedType, actualType)
};
}
}
});
};
exports.validateFields = validateFields;
//# sourceMappingURL=schema-validation.js.map