I've created a function to get the current user's data from the Reddit API, as part of a Reddit object with multiple functions.
async getCurrentUserId() {
if (userID) return userID;
const token = await Reddit.getAccessToken().then(val => {
return val;
})
const url = "https://oauth.reddit.com/api/v1/me"
const headers = {
"Authorization": `Bearer ${token}`,
"User-Agent": "blablabla",
};
const response = await fetch(url, { headers: headers });
if (response.ok) {
const jsonResponse = await response.json();
return jsonResponse.name;
}
},
However, when I try and extract the data, I keep getting a promise rather than the resolved value, and I can't seem to be able to figure it out.
const userID = Reddit.getCurrentUserId().then(val => {
return val;
}) // returns "PromiseĀ {<pending>}"
Assistance with this would be appreciated.
You either need to do your logic inside .then(), or simplify by using await:
const token = await Reddit.getAccessToken();
...
const userID = await Reddit.getCurrentUserId();
Related
I have this async function to get an API access token:
const getAccessToken = async () => {
try {
const body = new URLSearchParams({
grant_type: 'client_credentials',
scope: 'manage:all'
}).toString();
const config = {
headers: {
Content_Type: 'application/x-www-form-urlencoded'
},
auth: {
username: clientId,
password: clientSecret
}
};
const { data: res } = await axios.post(
`${baseUrl}/oauth2/token`,
body,
config
);
return res;
} catch (err) {
console.log(err);
}
};
It return a promise which I use in the following function to log the access token to the console:
getAccessToken().then(res => {
console.log(res.access_token);
});
It logs the token as a string.
Now I want to use this string value in another function in my code. Do I just call the function where I need the value like above and replace the console.log with return?
How do I use this string value in other parts of my code?
One way of doing it to call the function everywhere,
async function anotherFunction() {
const res = await getAccessToken();
console.log(res.access_token)
}
Another way of doing this is to call the function on your page load save the token in session/local storage/cookies and use the token in other functions by fetching it from session/local storage/cookies to avoid making multiple API calls.
On page load
const res = await getAccessToken();
localStorage.setItem('token', res.access_token);
async function anotherFunction() {
const res = localStorage.getItem('token');;
console.log(res.access_token)
}
Here is the function that I wanna test, it takes a token and a description as props. Normally in React code, I can get token from useContext.
export const updateUserProfileAbout = async (
token,
description
) => {
const dataUpdateTemplateDescriptionRes = await patchData(`me/`, token, {
about:description,
});
const dataUpdateTemplateDescriptionJson = await dataUpdateTemplateDescriptionRes.json();
return dataUpdateTemplateDescriptionJson;
};
And here is my custom patchData function:
const patchData = async (urn, token, data = "") => {
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${token.access}`,
};
const body = data ? JSON.stringify(data) : null;
let response;
if (body) {
response = await fetch(`${host}/api/${urn}`, {
method: "PATCH",
headers,
body,
});
} else {
response = await fetch(`${host}/api/${urn}`, {
method: "PATCH",
headers,
});
}
if (!response.ok) throw new Error(response.status);
return response;
};
You are right. You don't need the token. All you need to do for mocking the fetch is the following:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.resolve()) as jest.Mock);
If you want to retrieve a specific object from a json response, you can use:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ myObject }) })) as jest.Mock);
You can also reject it to trigger the error catch:
jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.reject()) as jest.Mock);
If you want to return something multiple times, change the mockImplementationOnce to whatever you need (maybe mockImplementation, for returning it every time you call it).
If you also want to expect the call of the fetch just add a constant:
const myFetch = jest.spyOn(global, 'fetch').mockImplementationOnce(
jest.fn(() => Promise.reject()) as jest.Mock);
You can then expect it via: expect(myFetch).toBecalledTimes(1);
After one more day of researching, I might be wrong though but I don't think I have to care about token or authorization when unit testing for front-end. All I need is jest.fn() to mock function and jest.spyOn(global, "fetch") to track fetch API.
For more information, here are some references that I read:
https://codewithhugo.com/jest-fn-spyon-stub-mock/
https://dev.to/qmenoret/mocks-and-spies-with-jest-32gf
https://www.pluralsight.com/guides/how-does-jest.fn()-work
https://www.loupetestware.com/post/mocking-api-calls-with-jest
I'm trying to implement the Stripe payment API in my website. However, I'm not very familiar with JavaScript, which is leading to some issues.
Most of my code is taken from Stripe's website, with some modifications made to better suit my own site.
I have a function CreateCustomer, which is intended to create a customer based on input from a form:
async function createCustomer () {
var emailJSON = {
"email": `${document.querySelector('#email-field').value}`
};
const output = await fetch('/create-customer', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(emailJSON)
})
.then((response) => {
return response.json();
})
.then((result) => {
return result;
});
return output;
}
This function is called by another function, and the output is stored because I have another function which requires the customer's Id.
var handleForm = function () {
// other code
const customerPromise = createCustomer();
var customer = customerPromise.data;
// more code
}
The fetch call in CreateCustomer calls the following C# code:
[Route ("create-customer")]
[ApiController]
public class CustomerCreationController : Controller
{
[HttpPost]
public ActionResult Create(CustomerCreateRequest request)
{
var customers = new CustomerService();
var customer = customers.Create(new CustomerCreateOptions
{
Email = request.Email,
});
return Json(new { _customer = customer.Id });
}
public class CustomerCreateRequest
{
[JsonProperty("email")]
public string? Email { get; set; }
}
}
From what I understand about Promises, if I call on the promise before it's resolved, I will get "undefined". But I also think I understand that if I await the promise (as I have done with the fetch in CreateCustomer), this shouldn't be an issue. However, whenever I run this code, I end up saving undefined in my customer variable in handleForm.
Like I said, I'm pretty unfamiliar with JavaScript, so the problem is probably some quirk of the language and/or Promises that I've overlooked. Thanks in advance for your help.
If your createCustomer is async, you need to await it, when calling it:
var handleForm = async function () {
// other code
const customerPromise = await createCustomer();
var customer = customerPromise.data;
// more code
}
#Marco is correct.
You also doing await and including .then witch is redundant.
Here are your options for promises:
// Using async/await
async function createCustomer () {
try {
const requestBody = {
email: `${document.querySelector('#email-field').value}`
};
const response = await fetch('/create-customer', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
})
const data = await response.json()
return data;
} catch (error) {
console.error(error);
}
}
/*
If you are calling createCustomer somewhere it has to
either be a async function to allow await, or
use .then((data) => console.log(data))
*/
const data = await createCustomer();
// Promise chains
async function createCustomer (callback) {
const requestBody = {
email: `${document.querySelector('#email-field').value}`
};
fetch('/create-customer', {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(requestBody)
})
.then(response => response.json())
.then(data => callback(data))
.catch(e => console.error(e))
}
createCustomer((data) => {
// do something with data
})
One little thing with fetch is that you also have to await the .json() call. Since that is also a promise.
If you are doing a lot of network requests maybe take a look at axios.
Currently I am trying to convert a working fetch POST request into an Axios POST request. However, I keep getting this Error -> 'Uncaught (in promise) Error: Request failed with status code 400'.
This is my working fetch request code below:
async function postData(url ='', data = { }) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json' },
body: JSON.stringify(data) });
let txoData = await response.json();
let a = txoData.payload.substring(68,134);
console.log(a);
console.log(txoData)
}
postData('https://merchantapi.taal.com/mapi/tx', { 'rawtx': rawtx })
.then(txoData => { return txoData })
.then(a => { return a })
and this is my NOT working convert to Axios attempt below:
async function postData() {
const response = await axios.post('https://merchantapi.taal.com/mapi/tx', {'rawtx': rawtx});
let txoData = await response.json();
let a = txoData.payload.substring(68,134);
console.log(a);
console.log(txoData)
}
postData()
.then(txoData => { return txoData })
.then(a => { return a })
I would greatly appreciate any help on this issue :)) x
To pass custom headers while using axios supply an object containing the headers as the last argument
I am trying to fetch data from the Storm Glass API. I am using their template Fetch request (https://docs.stormglass.io/?javascript#point-request).
When I run the script the console reads out "Promise { <pending> }" indefinitely. So, the request is not returning a value but I can't work out why. Any ideas?
I have replaced my API key with <My API key>
const http = require('http')
const fetch = require('isomorphic-fetch');
http.createServer((req, res) => {
////////////////////////////////////////////////App code
const lat = 58.7984;
const lng = 17.8081;
const params = 'waveHeight,airTemperature';
fetch(`https://api.stormglass.io/point?lat=${lat}&lng=${lng}¶ms=${params}`, {
headers: {
'Authorization': '<My API key>'
}
}).then(function(response) {
// Do something with response data.
const jsonData = response.json();
console.log(jsonData)
});
/////////////////////////////////////////////////////////
}).listen(3000);
console.log("service running on http://localhost:3000");
The response.json function return a Promise, not the deserialized object. Your code should read:
fetch(`https://api.stormglass.io/point?lat=${lat}&lng=${lng}¶ms=${params}`, {
headers: {
'Authorization': '<My API key>'
}
})
.then(response => response.json())
.then(function(jsonData) {
// Do something with response data
console.log(jsonData)
});
As an aside to gretro's answer, you may have got the idea that const json = response.json() would work from looking at async/await code as it's very similar, so here's how that code might look if written that way. It's traditionally wrapped in a try/catch, so I've included that too.
http.createServer(async (req, res) => {
const lat = 58.7984;
const lng = 17.8081;
const params = 'waveHeight,airTemperature';
try {
const endpoint = `https://api.stormglass.io/point?lat=${lat}&lng=${lng}¶ms=${params}`;
const params = { headers: { 'Authorization': '<My API key>' } };
const response = await fetch(endpoint, params);
const jsonData = await response.json();
console.log(jsonData);
} catch (err) {
console.error(err);
}
}).listen(3000);
You can resolve promise by using async/awiat.
fetch(`https://api.stormglass.io/point?lat=${lat}&lng=${lng}¶ms=${params}`, {
headers: {
'Authorization': '<My API key>'
}
})
.then(async response => {
const jsonData = await response.json();
console.log(jsonData)
})