Andras Bacsai 142b83cc13
v1.0.7 (#32)
New features:
- Automatic error reporting (enabled by default)
- Increase build times by leveraging docker build caches
- 
Fixes:
- Fix error handling
- Fix vue autodetect
- Custom dockerfile is not the default

Others:
- Cleanup `logs-servers` collection, because old errors are not standardized
- New Traefik proxy version
- Standardized directory configurations
2021-04-19 09:46:05 +02:00

240 lines
10 KiB
JavaScript

/* eslint-disable */
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.compileResponseValidationSchema = exports.addResponseValidation = exports.convertValidationErrors = exports.validationMessagesFormatters = exports.niceJoin = void 0;
const ajv_1 = __importDefault(require("ajv"));
const http_errors_enhanced_1 = require("http-errors-enhanced");
const interfaces_1 = require("./interfaces");
const utils_1 = require("./utils");
function niceJoin(array, lastSeparator = ' and ', separator = ', ') {
switch (array.length) {
case 0:
return '';
case 1:
return array[0];
case 2:
return array.join(lastSeparator);
default:
return array.slice(0, array.length - 1).join(separator) + lastSeparator + array[array.length - 1];
}
}
exports.niceJoin = niceJoin;
exports.validationMessagesFormatters = {
contentType: () => 'only JSON payloads are accepted. Please set the "Content-Type" header to start with "application/json"',
json: () => 'the body payload is not a valid JSON',
jsonEmpty: () => 'the JSON body payload cannot be empty if the "Content-Type" header is set',
missing: () => 'must be present',
unknown: () => 'is not a valid property',
uuid: () => 'must be a valid GUID (UUID v4)',
timestamp: () => 'must be a valid ISO 8601 / RFC 3339 timestamp (example: 2018-07-06T12:34:56Z)',
date: () => 'must be a valid ISO 8601 / RFC 3339 date (example: 2018-07-06)',
time: () => 'must be a valid ISO 8601 / RFC 3339 time (example: 12:34:56)',
uri: () => 'must be a valid URI',
hostname: () => 'must be a valid hostname',
ipv4: () => 'must be a valid IPv4',
ipv6: () => 'must be a valid IPv6',
paramType: (type) => {
switch (type) {
case 'integer':
return 'must be a valid integer number';
case 'number':
return 'must be a valid number';
case 'boolean':
return 'must be a valid boolean (true or false)';
case 'object':
return 'must be a object';
case 'array':
return 'must be an array';
default:
return 'must be a string';
}
},
presentString: () => 'must be a non empty string',
minimum: (min) => `must be a number greater than or equal to ${min}`,
maximum: (max) => `must be a number less than or equal to ${max}`,
minimumProperties(min) {
return min === 1 ? 'cannot be a empty object' : `must be a object with at least ${min} properties`;
},
maximumProperties(max) {
return max === 0 ? 'must be a empty object' : `must be a object with at most ${max} properties`;
},
minimumItems(min) {
return min === 1 ? 'cannot be a empty array' : `must be an array with at least ${min} items`;
},
maximumItems(max) {
return max === 0 ? 'must be a empty array' : `must be an array with at most ${max} items`;
},
enum: (values) => `must be one of the following values: ${niceJoin(values.map((f) => `"${f}"`), ' or ')}`,
pattern: (pattern) => `must match pattern "${pattern.replace(/\(\?:/g, '(')}"`,
invalidResponseCode: (code) => `This endpoint cannot respond with HTTP status ${code}.`,
invalidResponse: (code) => `The response returned from the endpoint violates its specification for the HTTP status ${code}.`,
invalidFormat: (format) => `must match format "${format}" (format)`
};
function convertValidationErrors(section, data, validationErrors) {
const errors = {};
if (section === 'querystring') {
section = 'query';
}
// For each error
for (const e of validationErrors) {
let message = '';
let pattern;
let value;
let reason;
// Normalize the key
let key = e.dataPath;
if (key.startsWith('.')) {
key = key.substring(1);
}
// Remove useless quotes
/* istanbul ignore next */
if (key.startsWith('[') && key.endsWith(']')) {
key = key.substring(1, key.length - 1);
}
// Depending on the type
switch (e.keyword) {
case 'required':
case 'dependencies':
key = e.params.missingProperty;
message = exports.validationMessagesFormatters.missing();
break;
case 'additionalProperties':
key = e.params.additionalProperty;
message = exports.validationMessagesFormatters.unknown();
break;
case 'type':
message = exports.validationMessagesFormatters.paramType(e.params.type);
break;
case 'minProperties':
message = exports.validationMessagesFormatters.minimumProperties(e.params.limit);
break;
case 'maxProperties':
message = exports.validationMessagesFormatters.maximumProperties(e.params.limit);
break;
case 'minItems':
message = exports.validationMessagesFormatters.minimumItems(e.params.limit);
break;
case 'maxItems':
message = exports.validationMessagesFormatters.maximumItems(e.params.limit);
break;
case 'minimum':
message = exports.validationMessagesFormatters.minimum(e.params.limit);
break;
case 'maximum':
message = exports.validationMessagesFormatters.maximum(e.params.limit);
break;
case 'enum':
message = exports.validationMessagesFormatters.enum(e.params.allowedValues);
break;
case 'pattern':
pattern = e.params.pattern;
value = utils_1.get(data, key);
if (pattern === '.+' && !value) {
message = exports.validationMessagesFormatters.presentString();
}
else {
message = exports.validationMessagesFormatters.pattern(e.params.pattern);
}
break;
case 'format':
reason = e.params.format;
// Normalize the key
if (reason === 'date-time') {
reason = 'timestamp';
}
message = (exports.validationMessagesFormatters[reason] || exports.validationMessagesFormatters.invalidFormat)(reason);
break;
}
// No custom message was found, default to input one replacing the starting verb and adding some path info
if (!message.length) {
message = `${e.message.replace(/^should/, 'must')} (${e.keyword})`;
}
// Remove useless quotes
/* istanbul ignore next */
if (key.match(/(?:^['"])(?:[^.]+)(?:['"]$)/)) {
key = key.substring(1, key.length - 1);
}
// Fix empty properties
if (!key) {
key = '$root';
}
key = key.replace(/^\//, '');
errors[key] = message;
}
return { [section]: errors };
}
exports.convertValidationErrors = convertValidationErrors;
function addResponseValidation(route) {
var _a;
if (!((_a = route.schema) === null || _a === void 0 ? void 0 : _a.response)) {
return;
}
const validators = {};
/*
Add these validators to the list of the one to compile once the server is started.
This makes possible to handle shared schemas.
*/
this[interfaces_1.kHttpErrorsEnhancedResponseValidations].push([
this,
validators,
Object.entries(route.schema.response)
]);
// Note that this hook is not called for non JSON payloads therefore validation is not possible in such cases
route.preSerialization = async function (request, reply, payload) {
const statusCode = reply.raw.statusCode;
// Never validate error 500
if (statusCode === http_errors_enhanced_1.INTERNAL_SERVER_ERROR) {
return payload;
}
// No validator, it means the HTTP status is not allowed
const validator = validators[statusCode];
if (!validator) {
if (request[interfaces_1.kHttpErrorsEnhancedConfiguration].allowUndeclaredResponses) {
return payload;
}
throw new http_errors_enhanced_1.InternalServerError(exports.validationMessagesFormatters.invalidResponseCode(statusCode));
}
// Now validate the payload
const valid = validator(payload);
if (!valid) {
throw new http_errors_enhanced_1.InternalServerError(exports.validationMessagesFormatters.invalidResponse(statusCode), {
failedValidations: convertValidationErrors('response', payload, validator.errors)
});
}
return payload;
};
}
exports.addResponseValidation = addResponseValidation;
function compileResponseValidationSchema(configuration) {
// Fix CJS/ESM interoperability
// @ts-expect-error
let AjvConstructor = ajv_1.default;
/* istanbul ignore next */
if (AjvConstructor.default) {
AjvConstructor = AjvConstructor.default;
}
const hasCustomizer = typeof configuration.responseValidatorCustomizer === 'function';
for (const [instance, validators, schemas] of this[interfaces_1.kHttpErrorsEnhancedResponseValidations]) {
// @ts-expect-error
const compiler = new AjvConstructor({
// The fastify defaults, with the exception of removeAdditional and coerceTypes, which have been reversed
removeAdditional: false,
useDefaults: true,
coerceTypes: false,
allErrors: true
});
compiler.addSchema(Object.values(instance.getSchemas()));
compiler.addKeyword('example');
if (hasCustomizer) {
configuration.responseValidatorCustomizer(compiler);
}
for (const [code, schema] of schemas) {
validators[code] = compiler.compile(schema);
}
}
}
exports.compileResponseValidationSchema = compileResponseValidationSchema;