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)
}
Related
I cannot figure out how should I construct my code.
Basic info:
webhook (intercom) --> google cloud functions (await values) --> post message to slack.
Issue:
My code is working fine until I need to get an value from another await function and I am not sure how should I 'pause' the second await until the first one is complete.
Code:
// first function to get the information about agent
const getTeammateInfo = async function (teammate_id) {
try {
const response = await axios.get("https://api.intercom.io/admins/" + teammate_id, {
headers: {
'Authorization': "Bearer " + INTERCOM_API_AUTH_TOKEN,
'Content-type': "application/json",
'Accept': "application/json"
}
});
const { data } = response
return data
} catch (error) {
console.error(error);
}
};
// second function, which needs values from first function in order to find data
const slackID = async function (slack_email) {
if (slackID) {
try {
const response = await axios.get("https://api.intercom.io/admins/" + slack_email, {
headers: {
'Authorization': "Bearer " + SLACK_API_TOKEN,
'Content-type': "application/json",
'Accept': "application/json"
}
});
const { user } = response
return user
} catch (error) {
console.error(error);
}
}
};
It is used within the Google Cloud Function (
exports.execute = async (req, res) => {
try {
// map of req from intercom webhook
let {
data: {
item: {
conversation_rating: {
rating,
remark,
contact: {
id: customerId
},
teammate: {
id: teammateId
}
}
}
}
} = req.body
const teammateName = await getTeammateInfo(teammateId); // this works fine
const slackTeammateId = await slackID(teammateName.email) // this is where it fails I need to get the values from 'teammateName' in order for the function slackID to work
...
} catch (error) {
console.error(error)
res.status(500)
res.json({ error: error })
}
}
I have tried with Promise.all
const [teammateName, slackTeammateId] = await Promise.all([
getTeammateInfo(teammateId),
slackID(teammateName.email)
])
But I just cannot wrap my head around this how it should work.
Thank you.
// edit:
Code is okay, I just put the wrong API into the slackID function...
Thanks for double checking.
The problem with Promise.all() is that it fires both functions at the same time and waits until both are done, so you can't chain that way.
let teammateName
let slackTeammateId
getTeammateInfo(teammateId).then(r => {
teammateName = r
slackID(teammateName.email).then(r2 => {
slackTeammateId = r2
)}
);
then() method, on the other hand, waits until your method's return and then fires out everything in the callback function.
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'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();
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.
I am trying to pass a query string into my serverless function but it keeps returning an empty object.
search = (searchTerm) => {
// let url = `${URL}${searchTerm}`;
return fetch(`/.netlify/functions/token-hider?search=${searchTerm}`)
.then((response) => response.json())
.then((result) => {
console.log(result.results);
return results;
});
form.addEventListener("submit", (e) => {
e.preventDefault();
let searchTerm = input.value;
search(searchTerm);
});
const axios = require("axios");
const qs = require("qs");
exports.handler = async function (event, context) {
// apply our function to the queryStringParameters and assign it to a variable
const API_PARAMS = qs.stringify(event.queryStringParameters.search);
console.log(event);
// const API_PARAMS = qs.stringify(event.queryStringParameters);
console.log("API_PARAMS", API_PARAMS);
// Get env var values defined in our Netlify site UI
// TODO: customize your URL and API keys set in the Netlify Dashboard
// this is secret too, your frontend won't see this
const { KEY } = process.env;
const URL = `https://api.unsplash.com/search/photos?page=1&per_page=50&client_id=${KEY}&query=${API_PARAMS}`;
console.log("Constructed URL is ...", URL);
try {
const { data } = await axios.get(URL);
// refer to axios docs for other methods if you need them
// for example if you want to POST data:
// axios.post('/user', { firstName: 'Fred' })
return {
statusCode: 200,
body: JSON.stringify(data),
};
} catch (error) {
const { status, statusText, headers, data } = error.response;
return {
statusCode: error.response.status,
body: JSON.stringify({ status, statusText, headers, data }),
};
}
};
it works when i hard code the query string, and i can console log the search term and it is defined.
Since Netlify redirect mechanism is not able to provide you the data of which rule it matched, you could try to match the original request in your function to determine what it should do.
Hope this helps you solve your specific issue!
Here is the reference