Two custom methods/endpoints using loopBack, one works, the other gives a 401 - react-native

I created two custom endpoints with Loopback.
Account.deleteAllHearingTests = function (req, callback) {
console.log('here comes the req to delete all hearing tests', req);
Account.findById(req.accessToken.userId)
.then(account => {
if (!account) {
throw new Error('cannot find user');
}
return app.models.HearingTest.updateAll({ accountId: account.id }, { isDeleted: new Date() });
})
.then(() => {
callback(null);
})
.catch(error => {
callback(error);
})
}
Account.remoteMethod(
'deleteAllHearingTests', {
http: {
path: '/clearHearingTests',
verb: 'post'
},
accepts: [
{ arg: 'req', type: 'object', http: { source: 'req' } }
],
returns: {}
}
);
the second one looks like this.
Account.deleteSingleHearingTest = function (req, callback) {
// console.log('accounts.js: deleteSingleHearingTest: are we being reached????', req)
Account.findById(req.accessToken.userId)
.then(account => {
if (!account) {
throw new Error('Cannot find user');
}
console.log('account.js: deleteSingleHearingTest: req.body.hearingTestId N: ', req.body.hearingTestId);
return app.models.HearingTest.updateAll({ accountId: account.id, id: req.body.hearingTestId }, { isDeleted: new Date() });
})
.then(() => {
callback(null);
})
.catch(error => {
callback(error);
});
}
Account.remoteMethod(
'deleteSingleHearingTest', {
http: {
path: '/deleteSingleHearingTest',
verb: 'post'
},
accepts: [
{ arg: 'req', type: 'object', description: 'removes a single hearing test', http: { source: 'req' } }
],
description: 'this is the end point for a single delete',
returns: {}
}
);
};
The first custom method returns a 401 status response when I make the initial fetch. The second returns a 200.
Inside my actions file the first method is called with something that looks like this:
export function deleteAllHearingTests() {
return (dispatch, getState) => {
let state = getState();
if (!state.user || !state.user.accessToken || !state.user.accessToken.id || !state.user.accessToken.userId) {
console.debug('deleteAllHearingTests', state.user);
// TODO: ERROR
return;
}
fetch(SERVERCONFIG.BASEURL + '/api/Accounts/clearHearingTests?access_token=' + state.user.accessToken.id, {
method: 'POST',
headers: SERVERCONFIG.HEADERS
})
.then(response => {
console.log('here is your response', response);
if (response.status !== 200) {
throw new Error('Something is wrong');
}
return response.json()
})
the second method is called with
export const deleteSingleHearingTest = (hearingTestNumber) => {
return (dispatch, getState) => {
let state = getState();
if (!state.user || !state.user.accessToken || !state.user.accessToken.id || !state.user.accessToken.userId) {
console.debug('writeTestResult', state.user);
// TODO: ERROR
return;
}
console.log('single delete ', SERVERCONFIG.BASEURL + '/api/Accounts/deleteSingleHearingTest?access_token=' + state.user.accessToken.id)
fetch(SERVERCONFIG.BASEURL + '/api/Accounts/deleteSingleHearingTest?access_token=' + state.user.accessToken.id, {
method: 'POST',
headers: SERVERCONFIG.HEADERS,
body: JSON.stringify({ "hearingTestId": hearingTestNumber })
})
.then(response => {
console.log('getting response from initial fetch inside deleteSingleReqport', response);
They are nearly identical, however, one works..the other fails. What are some possible causes for the 401?

Did you try to call those methods with external tool like a postman, so you would exactly know if you don't miss access_token or something else? Also, when you compare code from one function and another, you can see that you are colling the updateAll with different arguments. It's hard to say without original code, but maybe the issue is there? Compare below:
return app.models.HearingTest.updateAll(
{ accountId: account.id },
{ isDeleted: new Date() });
return app.models.HearingTest.updateAll(
{ accountId: account.id, id: req.body.hearingTestId },
{ isDeleted: new Date() });
Additionally, in fetch method they are also diffferences, you are missing in one case the below:
body: JSON.stringify({ "hearingTestId": hearingTestNumber })
What you could also do to debug and to provide more data is to run server in debug mode by calling:
export DEBUG=*; npm start

Related

How to encode and parse / decode a nested query string Javascript

I'm sending form data from React Hook Form to Netlify via their submission-created function. I don't have any problem with encoding individual form field values, but now I'm trying to encode an array of objects.
Here is an example of my form data:
{
_id: "12345-67890-asdf-qwer",
language: "Spanish",
formId: "add-registration-form",
got-ya: "",
classType: "Private lessons",
size: "1",
days: [
{
day: "Monday",
start: 08:00",
end: "09:30"
},
{
day: "Wednesday",
start: "08:00",
end: "09:30"
}
]
}
The only problem I have is with the "days" array. I've tried various ways to encode this and this is the function I've currently been working with (which isn't ideal):
const encode = (data) => {
return Object.keys(data).map(key => {
let val = data[key]
if (val !== null && typeof val === 'object') val = encode(val)
return `${key}=${encodeURIComponent(`${val}`.replace(/\s/g, '_'))}`
}).join('&')
}
I tried using a library like qs to stringify the data, but I can't figure out how to make that work.
And here is the function posting the data to Netlify:
// Handles the post process to Netlify so I can access their serverless functions
const handlePost = (formData, event) => {
event.preventDefault()
fetch(`/`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: encode({ "form-name": 'add-registration-form', ...formData }),
})
.then((response) => {
if(response.status === 200) {
navigate("../../")
} else {
alert("ERROR!")
}
console.log(response)
})
.catch((error) => {
setFormStatus("error")
console.log(error)
})
}
Finally, here is a sample of my submission-created file to receive and parse the encoded data:
const sanityClient = require("#sanity/client")
const client = sanityClient({
projectId: process.env.GATSBY_SANITY_PROJECT_ID,
dataset: process.env.GATSBY_SANITY_DATASET,
token: process.env.SANITY_FORM_SUBMIT_TOKEN,
useCDN: false,
})
const { nanoid } = require('nanoid');
exports.handler = async function (event, context, callback) {
// Pulling out the payload from the body
const { payload } = JSON.parse(event.body)
// Checking which form has been submitted
const isAddRegistrationForm = payload.data.formId === "add-registration-form"
// Build the document JSON and submit it to SANITY
if (isAddRegistrationForm) {
// How do I decode the "days" data from payload?
let schedule = payload.data.days.map(d => (
{
_key: nanoid(),
_type: "classDayTime",
day: d.day,
time: {
_type: "timeRange",
start: d.start,
end: d.end
}
}
))
const addRegistrationForm = {
_type: "addRegistrationForm",
_studentId: payload.data._id,
classType: payload.data.classType,
schedule: schedule,
language: payload.data.language,
classSize: payload.data.size,
}
const result = await client.create(addRegistrationForm).catch((err) => console.log(err))
}
callback(null, {
statusCode: 200,
})
}
So, how do I properly encode my form data with a nested array of objects before sending it to Netlify? And then in the Netlify function how do I parse / decode that data to be able to submit it to Sanity?
So, the qs library proved to be my savior after all. I just wasn't implementing it correctly before. So, with the same form data structure, just make sure to import qs to your form component file:
import qs from 'qs'
and then make your encode function nice and succinct with:
// Transforms the form data from the React Hook Form output to a format Netlify can read
const encode = (data) => {
return qs.stringify(data)
}
Next, use this encode function in your handle submit function for the form:
// Handles the post process to Netlify so we can access their serverless functions
const handlePost = (formData, event) => {
event.preventDefault()
fetch(`/`, {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: encode({ "form-name": 'add-registration-form', ...formData }),
})
.then((response) => {
reset()
if(response.status === 200) {
alert("SUCCESS!")
} else {
alert("ERROR!")
}
console.log(response)
})
.catch((error) => {
console.log(error)
})
}
Finally, this is what your Netlify submission-created.js file should look like more or less:
const sanityClient = require("#sanity/client")
const client = sanityClient({
projectId: process.env.GATSBY_SANITY_PROJECT_ID,
dataset: process.env.GATSBY_SANITY_DATASET,
token: process.env.SANITY_FORM_SUBMIT_TOKEN,
useCDN: false,
})
const qs = require('qs')
const { nanoid } = require('nanoid');
exports.handler = async function (event, context, callback) {
// Pulling out the payload from the body
const { payload } = JSON.parse(event.body)
// Checking which form has been submitted
const isAddRegistrationForm = payload.data.formId === "add-registration-form"
// Build the document JSON and submit it to SANITY
if (isAddRegistrationForm) {
const parsedData = qs.parse(payload.data)
let schedule = parsedData.days
.map(d => (
{
_key: nanoid(),
_type: "classDayTime",
day: d.day,
time: {
_type: "timeRange",
start: d.start,
end: d.end
}
}
))
const addRegistrationForm = {
_type: "addRegistrationForm",
submitDate: new Date().toISOString(),
_studentId: parsedData._id,
classType: parsedData.classType,
schedule: schedule,
language: parsedData.language,
classSize: parsedData.size,
}
const result = await client.create(addRegistrationForm).catch((err) => console.log(err))
}
callback(null, {
statusCode: 200,
})
}

How to passing result of http request inside async in ExpressJS?

I have below code
async send(user, data) {
const postData = {
'data': 'john',
'secret': 'secret'
};
const dataJson = JSON.stringify(postData);
const options = {
hostname: 'example.com',
path: '/send',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': dataJson.length
}
};
const req = https.request(options, (res) => {
let data = '';
console.log('Status Code:', res.statusCode);
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Body: ', JSON.parse(data));
});
}).on("error", (err) => {
console.log("Error: ", err.message);
});
req.write(dataJson);
req.end();
//---------------
let postResult = // HERE I WANT TO GET WHAT HTTP POST REQUESTED (e.g dataJson.body?)
//---------------
let result;
try {
result = await this.users.collection('users').updateOne(
{
_id: user
},
{
$set: {
// I WANT TO USE THAT HERE
data1 : postResult,
data2 : data2
}
},
{ maxTimeMS: consts.DB_MAX_TIME_USERS }
);
} catch (err) {
log.error('DB', 'UPDATEFAIL id=%s error=%s', user, err.message);
err.message = 'Database Error, failed to update user';
err.code = 'InternalDatabaseError';
throw err;
}
return { success: true };
}
How to get those data to outside variable?
I almost crazy about this, been searching on google and not found anything
I am using express and native-http to make http request, are there any native-curl maybe?
thank you very much for all the help
Your current code is using callback to retrieve result, so you can initiate data variable to outside callback function
let data = '';
const req = https.request(options, (res) => {
console.log('Status Code:', res.statusCode);
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log('Body: ', JSON.parse(data));
});
})
And also there are other easier way to make http request with nodejs. you can check axios that support Promise and async/await.
you can use syntax like this with axios
const response = await axios.get('/user?ID=12345');
way more easier.

Firebase Cloud Functions Call : error : Object message : "Bad Request" status : "INVALID_ARGUMENT"

first of all i am working with react-native
i wanted to use Custom Claims on my project since it seems to fit the role distribution i expect to use on my app.
after setting my app following the documentation i succeed on creating some functions
but, here's the thing, when i try to call a function by fetching the endpoint i always get this error :
in the console
error
:
Object
message
:
"Bad Request"
status
:
"INVALID_ARGUMENT"
in firebase console
addAdminRoleTest Request body is missing data. { email: 'dev#test.com' }
i couldn't find any answer to that except that i send wrong information from my fetch but i don't understand why.
i even tried to simplify my function only to get the data i sent but i had the exact same error
find below my cloud function & the calling method :
functions/index.js
exports.addAdminRole = functions.https.onCall((data, context) => {
// get user
return admin.auth().getUserByEmail(data.email).then(user => {
// if not already (admin)
if(user.customClaims && (user.customClaims).admin === true) {
return;
}
// add custom claim (admin)
return admin.auth().setCustomUserClaims(user.uid, {
admin: true
});
}).then(() => {
return {
message: `Bravo : ${data.email} fait partie de l'équipe Admins`
}
}).catch(err => {
return err;
});
});
simplified function :
exports.addAdminRoleTest = functions.https.onCall(data => {
console.log("parse data : "+JSON.parse(data));
return (
JSON.parse(data)
);
});
adminScreen.js
function httpAddAdminRole() {
const initRequest = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body:JSON.stringify({
email: 'dev#test.com'
})
}
console.log(initRequest);
return fetch('https://us-central1-my-project.cloudfunctions.net/addAdminRole', initRequest)
.catch(err => console.log(err))
.then(res => res.json())
.then(parsedRes => {
console.log(parsedRes);
});
}
in the end this was mostly json knowledge that missed me
my body should have data included
here's the answer i came to :
functions/index.js
exports.addAdminRole = functions.https.onCall((data, context) => {
const dataParsed = JSON.parse(data);
// get user
return admin.auth().getUserByEmail(dataParsed.email).then(user => {
// if not already (admin)
if(user.customClaims && (user.customClaims).admin === true) {
console.log(dataParsed.email + " is already an Admin");
return;
}
// add custom claim (admin)
return admin.auth().setCustomUserClaims(user.uid, {
admin: true
});
}).then(() => {
return {
message: `Bravo : ${dataParsed.email} is now an Admin`
}
}).catch(err => {
return err;
});
});
adminScreen.js
function httpAddAdminRole(mail) {
const initRequest = {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
},
body:JSON.stringify({
data:JSON.stringify({
email: mail
})
})
}
console.log(initRequest);
return fetch('https://us-central1-my-project.cloudfunctions.net/addAdminRole', initRequest)
.catch(err => console.log(err))
.then(res => res.json())
.then(parsedRes => {
console.log(parsedRes);
});
}

Catch error server response with #nuxtjs/auth

I'm trying to catch the error response for #nuxtjs/auth but it doesn't seem to return anything but undefined.
It refuses to login if I include the user so I want to know why it's returning undefined.
CONFIG:
auth: {
strategies: {
local: {
endpoints: {
login: {
url: 'http://127.0.0.1:80/api/login',
method: 'post',
propertyName: 'token'
},
logout: false,
user: {
url: 'http://127.0.0.1:80/api/me',
method: 'get',
propertyName: undefined
}
},
tokenRequired: true,
tokenType: 'bearer',
}
},
plugins: [
'#/plugins/auth.js'
]
},
PLUGIN:
export default function ({ app }) {
app.$auth.onError((error, name, endpoint) => {
console.error(name, error)
});
}
VIEW FUNCTION:
- both handleSuccess and handleFailure returns undefined.
login() {
this.toggleProcessing(0);
let payload = {
username: 'admin',
password: 'admin123'
}
let handleSuccess = response => {
console.log(response);
this.toggleProcessing(0);
}
let handleFailure = error => {
console.log(error);
this.toggleProcessing(0);
}
this.$auth.loginWith('local', { data: payload }).then(handleSuccess).catch(handleFailure);
},
You can use e.response
async login() {
try {
const login = {
username: this.username,
password: this.password
}
let response = await this.$auth.loginWith('local', { data: login })
console.log('response', response)
} catch (e) {
console.log('Error Response', e.response)
}
}
I fell into the same problem and after spending some time i found out a very good way to catch the response. The solution is to use the axios interceptor. Just replace your plugin file code with the following
export default function ({$axios, $auth}){
$axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
}
I'm not sure initially what might be wrong here because I can't see the complete nuxt.config.js and your full component but here are a few things to check:
#nuxtjs/axios is installed
Both axios and auth modules are registered in the modules section of nuxt.config.js:
modules: [
'#nuxtjs/axios',
'#nuxtjs/auth'
]
Also, ensure the middleware property for auth is set in the component/page component.
Ensure you're following the documentation on this page: https://auth.nuxtjs.org/getting-starterd/setup
Ive been using try -> this.$auth.loginWith to catch error server response with #nuxtjs/auth.
login() {
const data = { form };
try {
this.$auth
.loginWith("local", { data: data })
.then(api => {
// response
this.response.success = "Succes";
})
.catch(errors => {
this.response.error = "Wrong username/password";
});
} catch (e) {
this.response.error = e.message;
}
},
Specify the token field in the nuxt.config
strategies: {
local: {
endpoints: {
login: { // loginWith
url: "auth/login",
method: "post",
propertyName: "data.token" // token field
},
user: { // get user data
url: "auth/user",
method: "get",
propertyName: "data.user"
},
}
}
},
modules: ["#nuxtjs/axios", "#nuxtjs/auth"],

Using promise with GraphRequestManager

Does anyone have an example on how to use promise with GraphRequestManager?
I get Cannot read property then of undefined error in my action creator.
function graphRequest(path, params, token=undefined, version=undefined, method='GET') {
return new Promise((resolve, reject) => {
new GraphRequestManager().addRequest(new GraphRequest(
path,
{
httpMethod: method,
version: version,
accessToken: token
},
(error, result) => {
if (error) {
console.log('Error fetching data: ' + error);
reject('error making request. ' + error);
} else {
console.log('Success fetching data: ');
console.log(result);
resolve(result);
}
},
)).start();
});
}
I call the above using my action creator
export function accounts() {
return dispatch => {
console.log("fetching accounts!!!!!!");
dispatch(accountsFetch());
fbAPI.accounts().then((accounts) => {
dispatch(accountsFetchSuccess(accounts));
}).catch((error) => {
dispatch(accountsFetchFailure(error));
})
}
}
I get 'Success fetching data:' in the console along with the result before the error. So the API call is made successfully. The error is after fetching the accounts in fbAPI.accounts().then((accounts) which I think is due to GraphRequestManager returning immediately instead of waiting.
I have a solution for you.
My provider look like this :
FBGraphRequest = async (fields) => {
const accessData = await AccessToken.getCurrentAccessToken();
// Create a graph request asking for user information
return new Promise((resolve, reject) => {
const infoRequest = new GraphRequest('/me', {
accessToken: accessData.accessToken,
parameters: {
fields: {
string: fields
}
}
},
(error, result) => {
if (error) {
console.log('Error fetching data: ' + error.toString());
reject(error);
} else {
resolve(result);
}
});
new GraphRequestManager().addRequest(infoRequest).start();
});
};
triggerGraphRequest = async () => {
let result = await this.FBGraphRequest('id, email');
return result;
}
That works great ! I let you adapt my solution to your system.