How to manage self created error message instead of using default celebrate #hapi/joi code - joi

I have two files, one is api.js and other one is handler.js. For schema handling I am using celebrate module #hapi/joi
On api.js I wrote only the API name
On handler.js I wrote the API functionality.
api.js
//JOI Schema Validator Middleware.
router.use(celebrate({
body: Joi.object().keys({
post: Joi.string().max(10),
userid: Joi.string(),
})
}));
const handler = require('./handler');
router.post('/createpost', handler.createPost);
router.use(errors());
module.exports = router;
By this if error happens then i got the Response like this
{"statusCode":400,"error":"Bad Request","message":"child \"post\" fails because [\"post\" length must be less than or equal to 10 characters long]","validation":{"source":"body","keys":["post"]}}
I just want to Convert this error into my own format error i.e something like this
{error: true, status: 500, message: 'validation error', version: x.x.2}
The default joi error is generated through router.use(errors()); this module. How I modify this?
Any help or suggestion is really appreciated.

TL;DR: Create your own 'errors()' function.
You have probably managed to change it by now, but just like me, I had the exact same issue and found this answerless thread.
Well, for future readers, celebrate errors() is nothing else than a function, more exactly, this one:
(err, req, res, next) => {
// If this isn't a Celebrate error, send it to the next error handler
if (!isCelebrate(err)) {
return next(err);
}
const {
joi,
meta,
} = err;
const result = {
statusCode: 400,
error: 'Bad Request',
message: joi.message,
validation: {
source: meta.source,
keys: [],
},
};
if (joi.details) {
for (let i = 0; i < joi.details.length; i += 1) {
const path = joi.details[i].path.join('.');
result.validation.keys.push(EscapeHtml(path));
}
}
return res.status(400).send(result);
}
There, you can see the response object 'result' being declared and how it's done. So, to change the output of it, you have to not use errors() and create your own function to handle it.
So, I declared a new function:
private errorHandling = (err, req, res, next) => {
if (isCelebrate(err)) {
return res.send({
statusCode: 400,
message: err.joi.message
});
}
return next(err);
}
You can obviously change the above to suit your needs.
Update
Celebrate changed their error structure to a CelebrateError, now you need access the error details using:
const errorBody = err.details.get('body'); // 'details' is a Map()
const { details: [errorDetails] } = errorBody;
instead of the err.joi. The message property remains the same.
Then, instead of using app.use(errors()) I used app.use(this.errorHandling), and now I get the celebrate response formatted as I want to.

After some research, I found out it can be solved 2 ways:
[Segments.BODY]: Joi.object().keys({
value: Joi.string().required().error(new Error('Value is required and has to be a text!')),
})
or
[Segments.BODY]: Joi.object().keys({
password: Joi.string().required().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).min(8).label('Password').messages({
'string.pattern.base': 'Your {#label} does not matche the suggested pattern',
'string.base': `Your {#label} should match the suggested pattern`,
'string.empty': `Your {#label} can not be empty`,
'string.min': `Your {#label} has to be at least {#limit} chars`,
'any.required': `Your {#label} is required`,
}),
})

Related

axios keeps removing question mark for query string

Here's my axios config:
const axiosApi = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL
})
axiosApi.interceptors.response.use(
response =>
response
,
error => {
if (error.response.status === 404) {
throw new Error(`404\nService does not exist\n${error.request.path}`)
}
}
)
export async function get(url) {
console.log(url)
return await
axiosApi.get(url, {
crossDomain: true
}).then(response => {
return response?.data
})
}
The problem is that when I try to get /path?key=value, I get the /pathkey=value does not exist error.
While console.log(url) shows me the true URL with the question mark and query string, the response interceptor strips the question mark out and that causes 404.
Any idea why this happens?
This is an Axios bug in v1.0.0
Issue reported here: https://github.com/axios/axios/issues/4999
Fixed in v1.1.0
You should pass the query params like below:
axios.get('/path', { params: { key: value} });
Try limit the version in dependency.
"dependencies": {
"axios": "~0.27.2",
},
may help.
Personally I don't even get why Axios developer decide to do this.
Basically according to HTTP Protocol both path and query parameters is considered URI.
Axios.get() should accept everything that is considered URI to conform with HTTP specifications.
And project which let user input its URL for fetch the data will just broken out of the box.

Google Action Webhook Inline Editor Returns Before the API call

This is my first Google Action project. I have a simple slot after the invocation. User enters the value on prompt and slot invokes the webhook and make a call to API using the user input. All works fine. However the webhook returns to users even before the API call finish processing and returns the value (line 1 conv.add). I do see in the logs that everything from API is logged fine after the webhook returns to user. Below is the code I am using. I am using inline editor. What am I missing? Thanks for help in advance.
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
var https = require('https');
const fetch = require('node-fetch');
const app = conversation({debug: true});
app.handle('SearchData', conv => {
const body = JSON.stringify({
val: "this is my body"
});
// prepare the header
var postheaders = {
'Content-Type' : 'application/json',
'Auth' : 'MyAuthCreds'
};
fetch('https://host.domain.com/data', {
method: 'post',
body: body,
headers: postheaders,
})
.then(res => res.json())
.then(d => {
console.log(d);
var profile = d;//JSON.parse(d);
console.log(d.entries);
console.log("Length: "+ d.entries.length);
if(d.entries.length > 0)
{
console.log("Data found");
conv.add("Data found"); //line 1
}
else
{
console.log("no data found");
conv.add("no data found"); //line 1
}
})
.catch(function (err) {
// POST failed...
console.log(err);
});
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
Your issue is that your handler is making API calls which are asynchronous, but the Assistant Conversation library doesn't know that you're doing so. So as soon as the handler finishes, it tries to send back a response, but your asynchronous responses (the stuff in the then() blocks) haven't executed yet.
To address this, you need to return a Promise object so the library knows to wait till the Promise is fulfilled before it returns.
Fortunately, in your case, this should be pretty straightforward. fetch and all the .then() blocks return a Promise. So all you need to do is add a return statement in front of the call to fetch. So something like this:
return fetch('https://host.domain.com/data', {

my delete method of axios didn't work while post method work

I am making likes, dislikes function.
everything is working fine, except this delete method 😐
at React :
const goDownLike = async () => {
const variables = {
fromWhom: user.userData._id,
toWhat: toWhat
};
await axios.delete('/api/heart/downLike', variables);
}
at Node.js :
router.delete('/downLike', (req, res) => {
Like.findOneAndDelete(req.body).exec((err, result) => {
if (err) return res.status(400).json({ success: false, err });
res.status(200).json({ success: true });
});
});
when I clicked heart everytime, it seemed like it deleted somebody's likes including mine 😐
so I changed like this:
router.delete('/downLike', (req, res) => {
const { fromWhom, toWhat } = req.body;
Like.findOneAndDelete({ fromWhom: fromWhom, toWhat: toWhat }).exec(
(err, result) => {
if (err) return res.status(400).json({ success: false, err });
res.status(200).json({ success: true });
}
);
});
but this is actually do not delete any likes, even mine!
I changed from this
Like.findOneAndDelete
to this
Like.deleteOne
and this is not working, too. it delete nothing.
so I changed delete method to post method
and finally it worked. 😐
my likes was deleted and somebody's heart was not.
but I don't want to use post method instead of delete method 😐
I want to keep the rules of RESTful.
What should I do?
What did I missed?
thanks for your helps.
The reason it works with POST and not DELETE is that the second parameter for axios.post() is the body, whereas the second parameter for axios.delete() is the axios config object. This is presumably because there has been some disagreement and change over time as to whether DELETE requests can or should have a body, and what should happen if they do.
You can pass data in the config object, as seen here.

set status code before returning in loopback

I have inherited some very messy loopback code and I want to know if it is possible to return a status code other than 200.
For example I have the following code:
PensionsUser.loginView = function (ctx, credentials, cb) {
var respJSON = util.getresponseTemplate();
credentials.email = credentials.email.toLowerCase().trim();
PensionsUser.login(credentials, 'user', function (err, loginResp) {
if (err) {
util.addErrorToResponse(err, respJSON, 'NO-PUS-LV-001');
app.log.error(util.loggingTemplate('PensionsUser', 'loginView', 'NO-PUS-LV-001', credentials.email));
ctx.res.status = 401; //does not work
cb(null, respJSON);
//etc.
I know cb(null, respJSON) should be returning the error like this cb(respJSON) but sadly the frontend code relies on this JSON being returned as currently is so my first step would be to just change the status code.
Is this possible?
You can set custom error like {error:500, message: "Custom Error"} or you can configure "strong-error-handler" in middleware config file.
For error you can set status like this:
callback({status: 401, message: 'Your error message'});
And for success status codes you someone already answered here: https://stackoverflow.com/a/26942431/1827886
To send response code according to your own requirements, you can use this code:
"YourModelName"."YourRemoteMethod" = function("YourAcceptArguments", callback) {
var error = new Error("New password and confirmation do not match");
error.status = "Enter the resposne code that you want to send";
// Example => error.status = 400
return callback(error);
};

Angular2 return data from validation service after Http call

I have build a validation service for my registration form and one of the static methods is checking if the entered email is available by calling my API the following:
static emailAvailable(control){
let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
let http = injector.get(Http);
let valid = "E-mail is available";
http.post('https://secretapi.com/email', JSON.stringify({ email: control.value }))
.map((res: Response) => res.json())
.subscribe(function(result){
if(result.success){
valid = result.success; //The console.log on the line below is correct, the one at the bottom of the script never changes.
console.log(valid);
return null; //Doesn't do anything?
}else{
valid = result.error; //The console.log on the line below is correct, the one at the bottom of the script never changes.
console.log(valid);
return { 'invalidEmailAddress': true }; //Doesn't do anything, just like the return above
}
});
console.log(valid); //Output always "E-mail is available"
}
It should return "null" to the form validator when the email is available. The last console.log at the bottom should output the message that it recieves in the subscribe call. This doesn't happen and I'm not sure why. For some reason everything that happens within the subscribe call is contained there and never reaches the validator. What should I change? I have no idea and been searching the web for hours now.
You have to return Observable or Promise from your validator:
return http.post('https://secretapi.com/email', ...
console.log(...) doesn't make any sense here, since it will be executed after the Observable has been created as an object, but not after the ajax call has bee made.
If you want to output something after a response has been received, you have to move it inside subscribe
So in the end this website had the right answer. Also important to notice with the Angular2 Form validator to put the Async validators in the third (3) parameter and not together in an array in the second (2) parameter. That took me about 3 hours to figure out.
function checkEmail(control: Control){
let injector = ReflectiveInjector.resolveAndCreate([HTTP_PROVIDERS]);
let http = injector.get(Http);
return new Observable((obs: any) => {
control
.valueChanges
.debounceTime(400)
.flatMap(value => http.post('https://secretapi.com/email', JSON.stringify({ email: control.value })))
.map((res: Response) => res.json())
.subscribe(
data => {
if(data.success){
obs.next(null);
obs.complete();
} else {
obs.next({ 'invalidEmailAddress': true });
obs.complete();
}
}
);
});
}
The validator should look something like this, with the first validators checking on required and if it's actually an email address and the last doing an async call to the server to see if it's not already in use:
this.registerForm = this.formBuilder.group({
'email': ['', [Validators.required, ValidationService.emailValidator], ValidationService.emailAvailable],
});