Merge pull request #425 from trouble/graphql-errors

gql errors include formatted field validation data
This commit is contained in:
Dan Ribbens
2020-10-20 09:36:27 -04:00
committed by GitHub
5 changed files with 41 additions and 40 deletions

View File

@@ -5,11 +5,12 @@ const httpStatus = require('http-status');
* @extends Error
*/
class ExtendableError extends Error {
constructor(message, status, isPublic) {
constructor(message, status, data, isPublic) {
super(message);
this.name = this.constructor.name;
this.message = message;
this.status = status;
this.data = data;
this.isPublic = isPublic;
this.isOperational = true; // This is required since bluebird 4 doesn't append it anymore.
Error.captureStackTrace(this, this.constructor.name);
@@ -25,10 +26,11 @@ class APIError extends ExtendableError {
* Creates an API error.
* @param {string} message - Error message.
* @param {number} status - HTTP status code of error.
* @param {object} data - response data to be returned.
* @param {boolean} isPublic - Whether the message should be visible to user or not.
*/
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false) {
super(message, status, isPublic);
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, data, isPublic = false) {
super(message, status, data, isPublic);
}
}

View File

@@ -3,7 +3,7 @@ const APIError = require('./APIError');
class ValidationError extends APIError {
constructor(results) {
super(results, httpStatus.BAD_REQUEST);
super(`Bad request with ${results.length} errors`, httpStatus.BAD_REQUEST, results);
}
}

View File

@@ -1,22 +1,28 @@
const errorHandler = async (info, debug, afterErrorHook) => {
return Promise.all(info.result.errors.map(async (err) => {
// TODO: use payload logging
console.error(err.stack);
/**
*
* @param info
* @param debug
* @param afterErrorHook
* @returns {Promise<unknown[]>}
*/
const errorHandler = async (info, debug, afterErrorHook) => Promise.all(info.result.errors.map(async (err) => {
// TODO: use payload logging
console.error(err.stack);
let response = {
...err,
};
let response = {
message: err.message,
data: err?.originalError?.data,
};
if (afterErrorHook) {
({ response } = await afterErrorHook(err, response) || { response });
}
if (afterErrorHook) {
({ response } = await afterErrorHook(err, response) || { response });
}
if (debug && debug === true) {
response.stack = err.stack;
}
if (debug && debug === true) {
response.stack = err.stack;
}
return response;
}));
};
return response;
}));
module.exports = errorHandler;

View File

@@ -20,6 +20,7 @@ const initCollections = require('../collections/graphql/init');
const initGlobals = require('../globals/graphql/init');
const buildWhereInputType = require('./schema/buildWhereInputType');
const access = require('../auth/graphql/resolvers/access');
const errorHandler = require('./errorHandler');
class InitializeGraphQL {
constructor(init) {
@@ -93,31 +94,23 @@ class InitializeGraphQL {
this.schema = new GraphQLSchema(schema);
// this.errorExtensions = [];
// this.errorExtensionIteration = 0;
// this.extensions = async (info) => {
// const { result } = info;
// if (result.errors) {
// const afterErrorHook = typeof this.config.hooks.afterError === 'function' ? this.config.hooks.afterError : null;
// this.errorExtensions = await errorHandler(info, this.config.debug, afterErrorHook);
// }
// return null;
// };
this.extensions = async (info) => {
const { result } = info;
if (result.errors) {
const afterErrorHook = typeof this.config.hooks.afterError === 'function' ? this.config.hooks.afterError : null;
this.errorResponse = await errorHandler(info, this.config.debug, afterErrorHook);
}
return null;
};
}
init(req, res) {
this.errorResponse = null;
return graphQLHTTP(
async (request, response, { variables }) => ({
schema: this.schema,
// customFormatErrorFn: () => {
// const response = {
// ...this.errorExtensions[this.errorExtensionIteration],
// };
// this.errorExtensionIteration += 1;
// return response;
// },
// extensions: this.extensions,
customFormatErrorFn: () => (this.errorResponse),
extensions: this.extensions,
context: { req, res },
validationRules: [
queryComplexity({