adonis message modular message exception - node.js

i have handle exception in adonis but the message is not can not change base condition
what i expected is i can change message error in any controller
'use strict'
const { LogicalException } = require('#adonisjs/generic-exceptions')
class InvalidAccessException extends LogicalException {
/**
* Handle this exception by itself
*/
handle (error, { response }) {
return response.status(403).json({
error: error,
});
}
}
module.exports = InvalidAccessException
and in my controller if there is error i just trow error
throw new InvalidAccessException("user already register");
but the error message is always empty

Related

NestJS not handling error from thrown from service in controller

I have a simple setup where I'm calling a service method from the controller like so:
// companies.controller.ts
#Controller("company")
export class CompaniesController {
constructor(private companiesService: CompaniesService) {}
#Post("/:id/upload")
#UseInterceptors(FilesInterceptor("file"))
uploadFiles(
#Param("id") id: string,
#UploadedFiles() files: Array<Express.Multer.File>,
) {
// throw new HttpException("Company not found", HttpStatus.NOT_ACCEPTABLE);
// console.log(files);
try {
this.companiesService.uploadFiles(id, files);
console.log("didn't get error");
} catch (error) {
console.log("got error");
throw new HttpException("forbidden", HttpStatus.FORBIDDEN);
}
}
}
// companies.service.ts
#Injectable()
export class CompaniesService {
constructor(
private prisma: PrismaService,
private s3Service: S3Service,
private filesService: FilesService,
) {}
async uploadFiles(id: Company["id"], files: Array<Express.Multer.File>) {
const company = false // for testing
if (!company) {
console.log("Company not found");
throw new Error();
}
}
I'm running this by using nest start --watch.
When I call this endpoint, My app quits and I get the following logged to my console:
didn't get error
Company not found
/src/companies/companies.service.ts:54
throw new Error();
^
Error:
at CompaniesService.uploadFiles (/src/companies/companies.service.ts:54:13)
How come I can't catch the error I'm throwing in my controller? It's clearly not catching because it's logging company not found. Exception Filtering is supposed to be built in by default so I'm not sure why this isn't producing a 500?
You aren't awaiting the this.companiesService.uploadFiles(id, files); call, which means that it is an unhandled promise. If anything errors from it, it's an unhandled rejection. Just add async to the uploadFiles in the controller and await to thethis.companiesService.uploadFiles call and you should be good to go

How to handle error in async/await function?

I have a function like this:
async getPatient(patientId: string): Promise<PatientDTO> {
const patient = await PatientDAO.getPatients({ id: patientId })
if (patient.length === 0) {
throw new NotFoundError("Patient Not Found!")
}
return patient[0]
}
But I got an error
UnhandledPromiseRejectionWarning: Error: Patient Not Found!
This happened cause I have used async function. How can I make this code running properly?
In order to manage errors in an async function, you have to use a try/catch block:
async getPatient(patientId: string): Promise<PatientDTO> {
try {
const patient = await PatientDAO.getPatients({ id: patientId })
return patient[0]
} catch (error) {
// Do whatever you may want with error
throw error;
}
}
I should mention, that if you simply want to throw the error thats received from getPatients theres no need for a try/catch block at all. Its only needed if you wish to modify the error or perform an extra action according to the error that was thrown.
You have 2 options:
First one is try/catch block with await keyword. Please notice that await has to be used in async function.
try {
const patient = await getPatient(foo);
// handle your data here
} catch(e) {
// error handling here
}
Second one is catch function
getPatient(foo)
.then(patient => {
// handle your data here
}).catch(error => {
// error handling here
});

How to assert the properties of a class inside a mock function with jest

I'm having issues with mocked functions that are passing a class as a parameter. For some reason, it doesn't seem to assert the content correctly
// ErrorHandler.js
class ErrorHandler extends Error {
constructor(status, reason) {
super();
this.status = status;
this.reason = reason;
}
}
export {
ErrorHandler
}
// express file express.js
const method = async (req, res, next) => {
try {
throw new ErrorHandler(401, 'error')
next()
} catch (error) {
next(error)
}
}
// test file
it('should call the next method with the proper error', async () => {
const request = {
body: {}
}
const next = jest.fn()
const response = mockResponse() // here it's just a simple mock
await method(request, response, next)
expect(next).toHaveBeenCalledWith(
// here the problem is that it doesn't seem to assert the parameters
// and this test is passing
new ErrorHandler('random text')
)
})
I tried mocking the ErrorHandler class but then It gives another error related that it can't compare the next method anymore
The problem is that Jest is trying to compare two error objects and doesn't really know how. You can see this with a simple assertion:
expect(new ErrorHandler(404, 'not found')).not.toEqual(new ErrorHandler(401, 'unauthorized'))
with the result:
expect(received).not.toEqual(expected) // deep equality
Expected: not [Error]
You need to be more specific, e.g.:
expect(next).toHaveBeenCalled();
const [err] = next.mock.calls[0];
expect(err).toMatchObject({ status: 401, reason: 'error' });

Axios interceptor retry http request when status is 200 and data is empty

I am short polling an endpoint until some data is ready i would like to retry the request up to 10 times.
When the data is not ready i recieve a 200 with an empty array.
When the data is ready i recieve a 200 with a non-empty array.
I use the following libraries
https://github.com/axios/axios
https://github.com/softonic/axios-retry
try {
const axiosInstance = axios.create();
axiosInstance.interceptors.response.use((response) => {
if (response.data.metrics.length === 0) {
const error = new Error("Metrics Not Ready");
return Promise.reject(error);
}
return response;
}, (error) => {
return Promise.reject(error);
});
axiosRetry(axiosInstance, {
retries: 10,
retryCondition: (error) => {
console.log("RETRY CONDITION", error);
},
});
const metrics = await axiosInstance(options);
} catch (error) {
console.log(error);
}
I have created an axios interceptor to check the length of the array if its 0 i am throwing an error. However this does not get caught by axiosRetry which at this point i want to retry the request. Instead it is being caught in the try catch block and ends.
You don't need axios-retry, all you need is axios and its response interceptor, with following steps:
Check whether data is empty. If yes, throw an axios.Cancel error. This would cancel the request, invoking error handler instead of success handler.
In the error handler, re-send the HTTP request if we haven't exceeded the maximum retry number (in your case, it is 10).
Keep running step 1 and 2 until we get data or have retried 10 times.
The code would look like below:
const axios = require('axios');
const MAX_RETRY = 10;
let currentRetry = 0;
function successHandler() {
console.log('Data is Ready');
}
function errorHandler() {
if (currentRetry < MAX_RETRY) {
currentRetry++;
console.log('Retrying...');
sendWithRetry();
} else {
console.log('Retried several times but still failed');
}
}
function sendWithRetry() {
axios.get('http://example.com')
.then(successHandler).catch(errorHandler);
}
axios.interceptors.response.use(function (response) {
if (response.data.metrics.length) {
throw new axios.Cancel('Operation canceled by the user.');
} else {
return response;
}
}, function (error) {
return Promise.reject(error);
});
sendWithRetry();
For axios-retry, unfortunately you cannot retry HTTP request when response status is 200 OK, as axios-retry uses axios.interceptors.response.use but does nothing for "successful response".
Here is the corresponding code in axios-retry's source file es/index.js. You can see that the interceptor of successful response is null:
export default function axiosRetry(axios, defaultOptions) {
axios.interceptors.request.use(config => {
const currentState = getCurrentState(config);
currentState.lastRequestTime = Date.now();
return config;
});
axios.interceptors.response.use(null, error => {
const config = error.config;
// If we have no information to retry the request
if (!config) {
return Promise.reject(error);
}
....

Correct way of handling promisses and server response

I am trying to improve my code in node.js / sail.js and I am fighting server response in promisses.
When you look at the first .then function you can see that method returns false in case of forbidden access or notFound. Then, in the next .then functions I must check if the return type is === false to skip to section and avoid sending http headers twice. Can this be improved somehow, to skip all next .then methods in case of failure? I can throw an Exception to go in the last .catch but then there must be a case to switch between all possible states. (i.e. forbidden, serverError or even not found)
Notification.findOne({id: req.param('id')})
.then(function(notification) {
if (!notification) {
res.notFound();
return false;
}
if (notification.triggeredBy != req.session.user.id) {
res.forbidden();
return false;
}
return notification;
})
.then(function(notification) {
if (notification === false) {
return false;
}
return Notification.update(notification.id, actionUtil.parseValues(req));
})
.then(function(notification) {
if (notification === false) {
return false;
}
res.json(notification);
})
.catch(function(err) {
sails.log(err);
res.serverError({message: 'A server error occurred.'});
})
If I would do this, first I seperate logic and receving/sending function. Second I specify listing of error codes. And it will be like that:
NotificationService.js
/*
Listing of error codes: {
* [1] Object not found
* [2] Forbidden
* [3] Server error
}
*/
module.exports = {
nameOfMethod: function(ID, sessionID) {
return new Promise(function(resolve, reject) {
Notification.findOne({ id: ID })
.then(function(notification) {
if (!notification) return reject({ error_code: 1 });
if (notification.triggeredBy !== sessionID) return reject({ error_code: 2 });
Notification.update(notification.id, actionUtil.parseValues(req))
.then(function(notification) {
return resolve(notification); // finally return our notification
})
.catch(function(err) {
sails.log.error(err); // It's good when log is classified. In this case is error
return reject({ message: 'A server error occurred.' });
});
})
.catch(function(err) {
sails.log.error(err);
return reject({ message: 'A server error occurred.' });
});
});
}
};
NotificationController.js
module.exports = {
notifyMe: function(req, res) {
const ID = req.param('id'), sessionID = req.session.user.id;
NotificationService.nameOfMethod(ID, sessionID)
.then(function(notification) {
return res.send(notification);
})
.catch(function(err) {
switch (err.error_code) {
case 1:
return res.notFound(err);
case 2:
return res.forbidden(err);
default:
return res.serverError(err);
}
});
}
};
In case where I use switch I think it is better way to select right response but on this time I haven't any idea
See how filtered .catch() is implemented in Bluebird - it can be useful in your case to throw all errors you need but avoid having a big switch/case block in the catch handler:
.catch(
class ErrorClass|function(any error)|Object predicate...,
function(any error) handler
) -> Promise
.caught(
class ErrorClass|function(any error)|Object predicate...,
function(any error) handler
) -> Promise
This is an extension to .catch to work more like catch-clauses in
languages like Java or C#. Instead of manually checking instanceof or
.name === "SomeError", you may specify a number of error constructors
which are eligible for this catch handler. The catch handler that is
first met that has eligible constructors specified, is the one that
will be called.
Example:
somePromise.then(function() {
return a.b.c.d();
}).catch(TypeError, function(e) {
//If it is a TypeError, will end up here because
//it is a type error to reference property of undefined
}).catch(ReferenceError, function(e) {
//Will end up here if a was never declared at all
}).catch(function(e) {
//Generic catch-the rest, error wasn't TypeError nor
//ReferenceError
});
See: http://bluebirdjs.com/docs/api/catch.html#filtered-catch
Instead of:
return false;
you can use:
return Promise.reject(someReason);
or:
throw someReason;
and you won't have to check for those false values - just use (possibly multiple) catch handlers.

Resources