easy question:
FunctionOutput: Promise {
_c:
[ { promise: [Object],
resolve: [Function],
reject: [Function],
ok: [Function],
fail: [Function],
domain: null } ],
_a: undefined,
_s: 1,
_d: true,
_v:
{ body:
{ token_type: 'bearer',
access_token: 'token',
expires_in: 7776000,
refresh_token: 'token' },
statusCode: 200 },
_h: 0,
_n: true }
This is my Output from a function and I want to specify output "access_token" How do I do that?
console.log("token is"+ data._v.body.access_token);
does not work...
Pls help Thanks a lot!
What you've shown is a promise. You'd use the promise via its then method:
data
.then(function(result) {
// Use result here
})
.catch(function(err) {
// Handle error here
});
We can't tell you how to access access_token on the result, because we don't know what part of what you've shown (if any) will be the resolution value. It may be result.access_token, or result.body.access_token. But you won't be able to access it except in a then callback.
data
.then(function(result) {
console.log(result.body.access_token);
})
.catch(function(err) {
// Handle error here
});
You can use destructuring if you just want to have the access_token
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
// whatever you're calling that returns that object
const mockRequest = () => new Promise(resolve => resolve(res))
// response
const res = {
body: {
token_type: 'bearer',
access_token: 'token',
expires_in: 7776000,
refresh_token: 'token'
},
statusCode: 200
}
/*
calls async function it then waits until its
finsihed the request and "then" calls the then with the data
Normally we would just return what ever comes back
i.e (data) => data.body.access_token
But we can use a new ES6 feature which just returns the object
name passed instead
i.e ({body}) => { token_type: 'bearer' ...
*/
function getAccess() {
mockRequest()
.then(({body: {access_token}}) => console.log(access_token))
.catch(err => console.log(err))
}
getAccess();
/*
// Or using es7 features such as async await
async function getAccessES7() {
const {body:{access_token}} = await mockRequest();
return access_token;
}
getAccessES7();
*/
Related
Added in a pre-requisite for the endpoint to validate that the client information being passed is legit or it will throw an error. The clientProfileValidation.clientProfileValidation method receives the request object and returns a profile object that gets attached to the request.pre.
When trying to update my route unit test, I get the below error.
Unhandled rejection occurred. One of your test may have failed silently.
TypeError: Cannot read properties of undefined (reading 'routes')
This is a nodejs api using HAPI framework. When I remove the pre from the route, the test passes. I attempted to mock the clientProfileValidation method but its not working as expected.
Route
const drayageRampRecommendation = {
method: 'POST',
path: '/endpoint',
handler: async (request, h) => {
try {
const resp = await rampHandler.rampRecommendation(request);
return h.response(resp).code(201);
} catch (error) {
return handleError(error).toBoom();
}
},
config: {
pre: [
{
method: clientProfileValidation.clientProfileValidation,
assign: 'profile'
}
],
payload: {
allow: ['application/json', 'application/*+json']
}
}
};
Unit Test:
Using the Tape and Test Double Libraries for testing
test('drayage/recommend-ramps route: should return 201 when successfully processed', async (t) => {
beforeEachRampRecommendation();
const options = {
method: 'POST',
url: '/endpoint',
payload: recommendRampFixture,
headers: { authorization: 'Bearer 123' },
auth: {
credentials: { user: 'test', clientId: 'testClient' },
strategy: 'default'
}
};
const testProfile = {
_id: 'testId',
auth0ClientName: 'test client'
};
td.when(clientProfileValidation.clientProfileValidation(), {
ignoreExtraArgs: true
}).thenReturn(testProfile);
td.when(recommendRampHandler.rampRecommendation(), {
ignoreExtraArgs: true
}).thenReturn('');
const server = await buildServer(routes);
const response = await server.inject(options);
t.equal(response.statusCode, 201, 'Should return 201 status code');
td.reset();
t.end();
});
As the title says, I got a 400 Bad Request error when I tried to get the code exchanged for an access token against https://app.asana.com/-/oauth_token.
I honestly can’t think of what could be causing this.
I will post the code and would appreciate anyone’s help.
const GetAsanaAccessToken = async (req, res) => {
const body = {
grant_type: 'authorization_code',
client_id: process.env.NEXT_PUBLIC_ASANA_CLIENT_ID,
client_secret: process.env.ASANA_CLIENT_SECRET,
redirect_uri: process.env.NEXT_PUBLIC_ASANA_REDIRECT_URI,
code: req.body.code // The code obtained in the previous flow goes here.
};
console.log({ body });
const url = 'https://app.asana.com/-/oauth_token';
const response = await fetch(url, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: JSON.stringify(body)
})
.then((res) => {
console.log({ res });
return res.json();
})
.catch((err) => {
console.log({ err });
return err;
});
res.status(200).json(response);
};
export default GetAsanaAccessToken;
Then { res } will be like this
{
res: Response {
size: 0,
timeout: 0,
[Symbol(Body internals)]: { body: [PassThrough], disturbed: false, error: null },
[Symbol(Response internals)]: {
url: 'https://app.asana.com/-/oauth_token',
status: 400,
statusText: 'Bad Request',
headers: [Headers],
counter: 0
}
}
}
I have asked the same question in the Asana forum, but since it is often unresolved, I have posted it here as well.
This is the cause.
body: JSON.stringify(body)
Replaced above with below, then worked.
body: Object.keys(body)
.map((key) => `${key}=${encodeURIComponent(body[key])}`)
.join('&');
Because the original one resulted in
{"grant_type":"authorization_code","client_id":"xxx","client_secret":"xxx","redirect_uri":"http://localhost:3000/","code":"xxx"}
It worked when I made it look like the one below (I just made it by hand, so it really encodes slashes, etc.) instead of the one above.
grant_type=authorization_code&client_id=xxx&client_secret=xxx&redirect_uri=http://localhost:3000/&code=xxx
I'm using this package as an API wrapper to interact with the CampaignMonitor API within a serverless function. The objective is to return a response to the caller of the serverless function, confirming whether the operation of adding a subscriber to CampaignMonitor was successful or not.
Here is what I have so far:
exports.handler = async (event, context) => {
const body = JSON.parse(event.body);
// set request details
const listId = process.env.CM_LIST_ID;
const details = body;
// Send Request and check for error returned
api.subscribers.addSubscriber(listId, details, (err, res) => {
if (err) {
return {
statusCode: 400,
body: JSON.stringify({ message: err }),
};
} else {
return {
statusCode: 200,
body: JSON.stringify({ message: 'success' }),
};
}
});
};
Unfortunately, this doesn't work, I think due to the fact that there is no await for the response to the final part where the request is sent. I'm a little unsure of how to handle it, with it being a callback function.
I've been playing with this code for a little while now and, if there is no error, the subscriber is added to the subscriber list and a success response is returned from the serverless function when the second return statement is outside of the callback (below api.subscribers.addSubscriber).
Problem
Returning a value from the callback function of api.subscribers.addSubscriber doesn't make it a return value of the wrapper handler function.
Solution
As the library you are using doesn't provide a promise-based API, you can create a promise wrapper around it and use that to get the desired result.
Easiest way to create a promise wrapper around something in nodeJS is to use the built-in util module.
const util = require("util");
const promisifedAddSubscriber = util.promisify(api.subscribers.addSubscriber);
Once you have a promise-based api.subscribers.addSubscriber function, you can await the call to this function.
exports.handler = async (event, context) => {
try {
...
// explicitly bind "this"
const result = await promisifedAddSubscriber.bind(api.subscribers)(listId, details);
return {
statusCode: 200,
body: JSON.stringify({ message: 'success' }),
};
}
catch (error) {
return {
statusCode: 400,
body: JSON.stringify({ message: error }),
};
}
};
find working implementation of async-await below:
exports.handler = async (event, context) => {
try
{
const body = JSON.parse(event.body);
// set request details
const listId = process.env.CM_LIST_ID;
const details = body;
const result = await api.subscribers.addSubscriber(listId, details);
return {
statusCode: 200,
body: JSON.stringify({ message: 'success' }),
};
}
catch(err){
return {
statusCode: 400,
body: JSON.stringify({ message: err }),
};
}
};
You need to return a Promise from check_saved_url. Inside of the Promise, you then need to use resolve to replace return. You can also use reject(new Error("error")) if there was an error.
You can read more about promises on MDN
exports.handler = async(event, context) => {
return new Promise((resolve, reject) => {
const body = JSON.parse(event.body);
// set request details
const listId = process.env.CM_LIST_ID;
const details = body;
// Send Request and check for error returned
api.subscribers.addSubscriber(listId, details, (err, res) => {
if (err) {
reject(new Error({
statusCode: 400,
body: JSON.stringify({
message: err
})
}));
} else {
resolve({
statusCode: 200,
body: JSON.stringify({
message: 'success'
})
});
}
});
});
};
I am trying to loop through 10 league of legends matches and for each match, call another api to get the match details. So far I have a function set up like this:
function getAllMatchData(ids) {
const promises = [];
_.take(ids, 1).forEach(id => {
const promise = fetch(`https://na1.api.riotgames.com/lol/match/v4/matches/${id}`, {
headers: {"X-Riot-Token": token}})
promises.push(promise);
})
return Promise.all(promises);
}
Since the api returns 100 matches, I only take the top 10 and then create an array of promises with them. I then do this to get the results:
getAllMatchData(_.map(data['matches'], 'gameId')).then(results => {
console.log(results);
}).catch(error => {
console.log(error);
})
But the console log for the results don't have any json data in it. It prints an array of objects that look like this:
Response {
size: 0,
timeout: 0,
[Symbol(Body internals)]: { body: [Gunzip], disturbed: false, error: null },
[Symbol(Response internals)]: {
url: 'https://na1.api.riotgames.com/lol/match/v4/matches/3556728982',
status: 200,
statusText: 'OK',
headers: [Headers],
counter: 0
}
}
I am not sure where the JSON data response is.
With fetch, you need to parse the response with the type of response you received.
const promise = fetch(`https://na1.api.riotgames.com/lol/match/v4/matches/${id}`, {
headers: {"X-Riot-Token": token}}).then(res => res.json())
In your case it's json. So it's as simple as calling res.json().
I guess that fetch returns a promise. But how do I handle it nicely? The code below does not quite work. I get {message: "Internal server error custom: TypeError: Cannot read property 'then' of undefined"}.
exports.handler = (event, context, callback) => {
try {
getDiscourseId(username, callback).then((userId) => {
callback(null, {
statusCode: 200,
headers: {},
body: JSON.stringify({
userId: userId
})
});
});
} catch (error) {
callback(null, {
statusCode: 500,
headers: {},
body: JSON.stringify({
message: "Internal server error custom: " + error
})
});
}
};
function getDiscourseId(username) {
console.log({username: username, discourseApiKey: discourseApiKey, discourseApiUser: discourseApiUser})
fetch(`https://${domain}/users/${username}.json?api_key=${discourseApiKey}&api_username=${discourseApiUser}`, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
method: 'GET',
})
.then(response => {
return response.json();
})
.then(data => {
if (data) {
return data.user.id;
}
})
.catch(err => {
return {err: err};
});
}
You're getting that error because your getDiscourseId function does not return a value.
If you add the keyword return in front of your fetch(...) call, you should start making some headway.
You'll probably also want to remove the .catch from inside getDiscourseId and instead add it to the end of the call to getDiscourseId inside your handler:
exports.handler = (event, context, callback) => {
getDiscourseId(username)
.then((userId) => {
callback(null, {
statusCode: 200,
headers: {},
body: JSON.stringify({
userId: userId
})
});
})
.catch(error => {
callback(null, {
statusCode: 500,
headers: {},
body: JSON.stringify({
message: "Internal server error custom: " + error
})
});
});
};
function getDiscourseId(username) {
console.log({username: username, discourseApiKey: discourseApiKey, discourseApiUser: discourseApiUser})
return fetch(`https://${domain}/users/${username}.json?api_key=${discourseApiKey}&api_username=${discourseApiUser}`, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
method: 'GET',
})
.then(response => {
if (!response.ok) { // h/t TJ Crowder
throw new Error("Failed with HTTP code " + response.status);
}
return response.json();
})
.then(data => {
if (data) {
return data.user.id;
}
});
}
EDIT: TJ Crowder is correct that you probably want to treat 4xx and 5xx responses as full-fledged errors. I shamelessly stole his example code from his blog and added it to the above.
Stop when you return the response.json(). This returns a promise, for which .then can be used.
You are returning the userId which .then cannot be used for.
If you stop at the return response.json(), you can use the '.then' statement that you already have (data.user.id => ...).