I seem to be having an issue with getting the expected response from a fetch call within a firebase cloud function. I'm sure it's due to my lack of knowledge on how the responses, promises, etc. work.
I'm trying to use atlassian crowd's rest api for SSO. If I use postman, I can get the desired results from the request. So I know that part of it is working.
What led me to using a cloud function is that making the same request using fetch was resulting in CORS issues from localhost. I figured if I can take the browser out of the equation, then the CORS issues would disappear. Which they have, but I'm not getting the desired response.
My cloud function looks like this:
const functions = require('firebase-functions');
const fetch = require('node-fetch');
const btoa = require('btoa');
const cors = require('cors')({origin:true});
const app_name = "app_name";
const app_pass = "app_password";
exports.crowdAuthentication = functions.https.onRequest((request, response)=>
{
cors(request, response, () =>{
let _uri = "https://my.server.uri/crowd/rest/usermanagement/1/session";
let _headers = {
'Content-Type':'application/json',
'Authorization':`Basic ${btoa(`${app_name}:${app_pass}`)}`
}
let _body = {
username: request.body.username,
password: request.body.password
}
const result = fetch(_uri, {
method: 'POST',
headers: _headers,
body: JSON.stringify(_body),
credentials: 'include'
})
response.send(result);
})
})
I'm then making the call in my application using fetch to the firebase endpoint and passing the username/password:
fetch('https://my.firebase.endpoint/functionName',{
method: 'POST',
body: JSON.stringify({username:"myusername",password:"mypassword"}),
headers: {
'Content-Type':'application/json'
}
})
// get the json from the readable stream
.then((res)=>{return res.json();})
// log the response - {size:0, timeout:0}
.then((res)=>
{
console.log('response: ',res)
})
.catch(err=>
{
console.log('error: ',err)
})
Thanks for looking.
Edit of May 2020
Note that request-promise is deprecated and I recommend to use axios.
Update following our discussion in the comments below
It appears that it doesn't work with the node-fetch library and that you should use another library like request-promise.
Therefore you should adapt your code as follows:
//......
var rp = require('request-promise');
exports.crowdAuthentication = functions.https.onRequest((request, response) => {
cors(request, response, () => {
let _uri = "https://my.server.uri/crowd/rest/usermanagement/1/session";
let _headers = {
'Content-Type': 'application/json',
'Authorization': `Basic ${btoa(`${app_name}:${app_pass}`)}`
}
let _body = {
username: request.body.username,
password: request.body.password
}
var options = {
method: 'POST',
uri: _uri,
body: _body,
headers: _headers,
json: true
};
rp(options)
.then(parsedBody => {
response.send(parsedBody);
})
.catch(err => {
response.status(500).send(err)
//.... Please refer to the following official video: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3
});
});
});
Initial answer with node-fetch
The fetch() method is asynchronous and returns a Promise. You therefore need to wait this Promise resolves before sending back the response, as follows:
exports.crowdAuthentication = functions.https.onRequest((request, response)=>
{
cors(request, response, () =>{
let _uri = "https://my.server.uri/crowd/rest/usermanagement/1/session";
let _headers = {
'Content-Type':'application/json',
'Authorization':`Basic ${btoa(`${app_name}:${app_pass}`)}`
}
let _body = {
username: request.body.username,
password: request.body.password
}
fetch(_uri, {
method: 'POST',
headers: _headers,
body: JSON.stringify(_body),
credentials: 'include'
})
.then(res => {
res.json()
})
.then(json => {
response.send(json);
}
.catch(error => {
//.... Please refer to the following official video: https://www.youtube.com/watch?v=7IkUgCLr5oA&t=1s&list=PLl-K7zZEsYLkPZHe41m4jfAxUi0JjLgSM&index=3
});
})
})
In addition, note that you need to be on the "Flame" or "Blaze" pricing plan.
As a matter of fact, the free "Spark" plan "allows outbound network requests only to Google-owned services". See https://firebase.google.com/pricing/ (hover your mouse on the question mark situated after the "Cloud Functions" title)
Related
So I moved over a non-reusable fetch request code snippet to my API:
let response = await fetch(visitURL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + userJWT
},
body: JSON.stringify(endingVisit)
});
if (response.ok) {
let {visitId, createdAt} = await response.json();
const viewVisitDto = new ViewVisitDto(`${visitId}${createdAt}${visitorId}${doctorId}${oldPatientId}`);
return viewVisitDto;
} else {
throw new Error("deactivated!")
}
I was able to get this far:
axios.post(visitURL, {
headers,
body: JSON.stringify(visit)
}).then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
})
But does not exactly give me the visitId and createdAt from the response and I cannot use a response.ok nor a response.json(). Essentially I need to pull out that visitId and createdAt that should be coming back in the response.
I also tried just using node-fetch library, but although in VS code it seems to accept it, TypeScript is not happy with it even when I do install #types/node-fetch and even when I create a type definition file for it, my API just doesn't like it.
Guessing what you are after is
// don't know axios, but if it returns a promise await it
const dto = await axios.post(visitURL, {
headers,
body: JSON.stringify(visit)
}).then((response) => {
// parse response
return {resonse.visitId, resonse.createdAt}
}).then(({visitId, createdAt}) => {
// form dto (where are other vals)?
return new ViewVisitDto(`${visitId}${createdAt}${visitorId}${doctorId}${oldPatientId}`);
}).catch((error) => {
console.log(error);
})
However - you don't mention where doctorId and oldPatientId come from... You try providing more info, including output of the console.log's and the surrounding code
I am trying to create a folder for a user, and I have been unsuccessful with api call attempts. My code is able to receive the correct access token, so I believe the be bug would be in createFolderTestFunction below.
async function redirectToDashboard() {
console.log("redirect to dashboard");
// var response = await requestTokenSilent();
var response;
if (!response || !response.status == 200) {
response = await requestTokenPopup();
}
if (response.accessToken) {
console.log(response);
createFolderTest(response.accessToken);
// location.href = hostname;
} else {
console.log("Unable to acquire token");
}
}
function createFolderTest(accessToken) {
var options = {
method: "POST",
headers: {
Authorization: accessToken,
"Content-Type": "application/json"
},
mode: "cors",
body: JSON.stringify({
displayName: "#COOLMONDAY"
})
};
var graphEndpoint = "https://outlook.office.com/api/v2.0/me/Inbox/";
fetch(graphEndpoint, options)
.then(resp => {
console.log(resp);
})
.catch(err => {
console.log(err);
});
}
A recommendation would be to get this working in Graph Explorer first. As this eliminates any issues with language being used and access token permissions.
https://developer.microsoft.com/en-us/graph/graph-explorer/preview
The Microsoft Graph endpoint is actually https://graph.microsoft.com/ , you can use the outlook url but moving forward Graph is where we invest in documentation, sdks and tooling.
As per the documentation https://learn.microsoft.com/en-us/graph/api/user-post-mailfolders?view=graph-rest-1.0&tabs=http
You should be using, you're missing 'mailfolders'
POST /me/mailFolders
You could also use our JavaScript SDK which makes mistakes like these a little easier with intellisense and strongly typed objects.
const options = {
authProvider,
};
const client = Client.init(options);
const mailFolder = {
displayName: "displayName-value"
};
let res = await client.api('/me/mailFolders')
.post(mailFolder);
https://learn.microsoft.com/en-us/graph/api/user-post-mailfolders?view=graph-rest-1.0&tabs=javascript
Im trying to fetch subcategories from my mvc application with reference to the category id with async fetch
I already fetched the categories and its all working
but when i try to fetch the subcategories with a post request it doesn't work!
//SubCategories
const categoriesLiList = document.querySelectorAll('.btn');
const getSubCategories = async () => {
const liBtnClick = list => {
nodeListForEach(list, cur => {
cur.addEventListener('click', () => {
debugger;
let categoryId = cur.value;
console.log(categoryId);
const getSubCategoriesById = async (url = ``, data = {}) => {
const subsResult = await fetch(url, {
method: "POST",
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json"
},
redirect: "follow",
referrer: "no-referrer",
body: JSON.stringify(data)
});
const subsData = await subsResult.json();
const populateSubCategories = arr => {
arr.forEach(cur => {
const subCategoriesLi = `
<li>${cur.Name}</li>
`;
document.querySelector('#subcategories').insertAdjacentHTML('beforeend', subCategoriesLi);
});
};
populateSubCategories(subsData);
};
getSubCategoriesById(`/controllername/jsonresult/ID`, { ID: categoryId });
});
});
};
liBtnClick(categoriesLiList);
};
getSubCategories();
The result should be the data from the api but its not reading the ID param.
what should i change in my post request??
EDIT: I am such an idiot lol my api wasn't working correctly, so for future purposes always test your apis with postman :)
also, there's no need for a post request! just a normal fetch get reques:
await fetch(`/controllerName/JsonResult/${categoryId}`);
I am such an idiot lol
my api wasn't working correctly, so for future purposes always test your apis with postman :)
also, there's no need for a post request! just a normal fetch get reques:
await fetch(`/controllerName/JsonResult/${categoryId}`);
const { user } = require('./config');
const axios = require('axios');
const Querystring = require('querystring');
let body = Querystring['stringify']({
email: 'MY EMAIL#email.com',
password: 'pass'
})
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
axios['post']('https://minecraftservers.org/login', body, config)
['then'](response => console.log(response))
Im trying to login through a website
it doesn't have an api
the headers are correct
if you're wandering how i knew this, i used chrome dev tools
like reverse engineer
content-type: application/x-www-form-urlencoded
that's the header they used when i tried to login to the site
this is what i get when i logged in through the site and not the code, it works there.
You can use URLSearchParams
const params = new URLSearchParams();
params.append('firstName', 'paul');
params.append('lastName', 'fred');
axios.post('/user', params);
It avoids adding another library.
I guess systax is your problem. Do you have any difficulties other than the syntax?
const { user } = require('./config');
const axios = require('axios');
const Querystring = require('querystring');
let body = Querystring['stringify']({
email: 'MY EMAIL#email.com',
password: 'pass'
})
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
axios.post('https://minecraftservers.org/login', body, config)
.then(response => console.log(response))
Try
axios.post('https://minecraftservers.org/login', body, config)
.then(response => console.log(response))
My Sequence is:
POST file to dropbox from local server
Delete file from local server (after successful POST)
Create a shareable link for the recently POSTed file
Send User the new shareable link
(Please note I am using the Dropbox API v2)
My Code:
I am using request-promise-native library.
let upload = (req,res) => {
let options = {
method: 'POST',
uri: 'https://content.dropboxapi.com/2/files/upload',
headers: {
'Authorization': 'Bearer Token here',
'Dropbox-API-Arg': "{\"path\": \"/test/"+req.file.originalname+"\",\"mode\": \"overwrite\",\"autorename\": true,\"mute\": false}",
'Content-Type': 'application/octet-stream'
},body: fs.createReadStream(`uploads/${req.file.originalname}`)
};
rp(options)
.then(() => {return _deleteLocalFile(req.file.originalname)})
.then(() => {return _generateShareableLink(req.file.originalname)}) // This makes a POST request to the Dropbox API and should return a link.
.then((shareableLink) => {sendJsonResponse(res, 200, shareableLink)})
.catch(function (err) {sendJsonResponse(res, 500, err)});
};
From my understanding (correct me if Im wrong) promises run in parallel and return a value overtime (whichever promise resolves first). How can I run promises in a specific sequence? Is my implementation of promises within best practices?
Your promises will work in sequence if they are used in chain.
I can suggest you also to use ES8 async/await features to get more beautiful code
let upload = async (req,res) => {
let options = {
method: 'POST',
uri: 'https://content.dropboxapi.com/2/files/upload',
headers: {
'Authorization': 'Bearer Token here',
'Dropbox-API-Arg': "{\"path\": \"/test/"+req.file.originalname+"\",\"mode\": \"overwrite\",\"autorename\": true,\"mute\": false}",
'Content-Type': 'application/octet-stream'
},body: fs.createReadStream(`uploads/${req.file.originalname}`)
};
try {
const request = await rp(options);
const dlf = await _deleteLocalFile(req.file.originalname);
const shareableLink= await _generateShareableLink(req.file.originalname);
const sendedResponse = await sendJsonResponse(res, 200, shareableLink);
} catch(e) {
await sendJsonResponse(res, 500, err);
}
}