Call method on object also do async operation before - javascript

i'm using a library that return an object with multiple keys and methods to use, one of the key of this object is accessToken.
accessToken value must exist before calling any method of this object (otherwise you're not authenticated).
The token is retrieved externally using an async axios function, also this token is only valid for 1 hour, in this case it's okay to get a new token every time you call some function inside it.
I dont' want to recreate the object every time i use this library (i'm using this library multiple times).
So far based on few articles i found online i did this:
const force = require('jsforce')
const { SFAuth } = require('./auth')
const common = require('../common')
class JSForce {
constructor (connection) {
return connection
}
static async Connect () {
const token = await SFAuth.getToken()
const connection = new force.Connection({
instanceUrl: common.SALESFORCE_URL,
accessToken: token
})
return new JSForce(connection)
}
}
const start = async () => {
const res = await JSForce.Connect()
console.log(res)
}
start()
If i try to do JSForce.Connect().sobject('Account') i get an error saying sobject is not a function.
It works if first i save JSFORCE.Connect() in a new instance and then i use this instance.sobject() but i can't do it every time i need to use it.
How would you solve this?
Thanks!!

Problem is field sobject will only come once you have JSForce connection successful. First we need to make sure we have that and we can save in variable. We will only call JSForce if we don't have instance already.
Declare a global variable in file.
let instance: <any>
// This method will return instance always if we dont have
const getInstance = async () => {
if (!instance) {
instance = await JSForce.Connect();
}
return instance;
};
const start = async () => {
const res = await getInstance().sobject('Account');
console.log(res);
}
start();

Related

Google cloud function onCreate not writing to the Database

I have created a google function for firebase that when A new conversation is added the function attaches it to the Users table under a new collection for each user in the conversation but when the function gets triggered nothing happens in the database, So far I have console logged the values tp make sure they ware being set right and they ware I have also tried looking at the google function logs and there are no errors according to the logs the script ran with no errors
Here is the code for the function
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
export const onConversationCreated = functions.firestore.document("Conversations/{conversationID}").onCreate((snapshot, context) => {
let data = snapshot.data();
let conversationID = context.params.conversationID;
if(data){
let members = data.members;
for(let index = 0; index < members.length; index++){
let currentUserID = members[index];
let remainingUserIDs = members.filter((u: string) => u !== currentUserID)
remainingUserIDs.forEach((m: string) => {
return admin.firestore().collection("Users").doc(m).get().then((_doc) => {
let userData = _doc.data();
if(userData){
return admin.firestore().collection("Users").doc(currentUserID).collection("Conversations").doc(m).create({
"conversationID": conversationID,
"image": userData.image,
"unseenCount": 1,
});
}
return null;
}).catch(() => {return null})
});
}
}
return null;
});
Can someone tell me if there is something wrong with my code or do I have to give functions permission to write to the cloud firestore database?
You are not dealing with promises correctly. Your function needs to return a promise that resolves with all of the asynchronous work is complete. Right now, it's just returning null, and not waiting for any work to complete.
All Firestore operations are asynchronous and return a promise. You will need to use these promises to build a single promise to return from the function. That means each time you query and write a document, that's generating another promise to handle.
Also, you should know that there is no method create() that you're trying to use to add a document. Perhaps you meant to use set() instead. The code will crash if it tries to execute create().

Await isn't waiting?

Hi I've got two functions. They're called inside another function. And I need the second function execute after the first is done.
I'm sorry this is probably a very simple question, but I'm still trying to wrap my head around promises and async/await in js. Can anyone help me figure out why this isn't working.
async function addDetails(){
const sub = await getSession();
// now wait for firstFunction to finish...
addProfileDetails(sub);
};
I have debug/logging statements in both. So I know that addProfileDetails is being executed before the variable sub is defined.
I'm using react native and node.js
EDIT:
export function getSession() {
console.log("checking user ...............................................")
Auth.currentSession().then(res=>{
console.log("retrieving token");
let accessToken = res.getAccessToken()
let payload = accessToken.payload;
console.log(`sub: ${payload.sub}`)
return payload.sub
})
};
This is my getSession() function. It also needs to be async?
You must return a promise from the function that you want to await
export function getSession() {
console.log("checking user ...............................................")
return Auth.currentSession().then(res=>{
console.log("retrieving token");
let accessToken = res.getAccessToken()
let payload = accessToken.payload;
console.log(`sub: ${payload.sub}`)
return payload.sub
})
};
Try this in your getSession
export function getSession() {
return new Promise((resolve, reject) => {
console.log("checking user ...............................................")
Auth.currentSession().then(res=>{
console.log("retrieving token");
let accessToken = res.getAccessToken()
let payload = accessToken.payload;
console.log(`sub: ${payload.sub}`)
resolve(payload.sub)
})
};

How can I keep a firebase function open when it's in another file

I have a file called db.js from which I make all my firebase calls.
I am calling a function in db.js from another file called home.js
How do I make it that the firebase connection stays open and the data gets passed back to home.js? I can't use a promise because that closes the connection.
Here is the function from db.js:
export function getShopNames() {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").on('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
return stores
})
}
and I call it from home like this:
let stores = db.getShopNames()
I want it to work so if a new store gets added to the real-time database, the variable updates
There is no concept of file based scope in JavaScript. The listener will stay active from the moment you call on('value', until you either call off on that same location or until you load a new page.
But your return stores doesn't do anything meaningful right now. It returns a value from the callback function that nobody will ever see/use.
Data is loaded from Firebase asynchronously, which means you can't return it from a function in the normal way. By the time the return statement runs, the data hasn't loaded yet. That's why you'll usually return a so-called promise, which then resolves when the data has loaded.
In your function that'd be:
export function getShopNames() {
return new Promise(function(resolve, reject) {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").once('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
resolve(stores);
}, (error) => {
reject(error);
})
}
Now you can call this function like this:
getShopNames().then((shopnames) => {
console.log(shopnames);
})
Update: you commented that you also want to handle updates to the shop names, you can't use once() and can't use promises (since those only resolve once).
Instead pass in a custom callback, and invoke that every time the shop names change:
export function getShopNames(callback) {
let userID = auth.currentUser.uid
let stores = []
userDB.ref('/users/' + userID + "/stores").once('value', snap => {
snap.forEach(storeNames => {
stores.push(storeNames.key)
})
callback(stores);
})
}
And then call it like:
getShopnames(function(shopnames) {
console.log(shopnames);
});

Async Function in AWS Lambda Alexa Intent with Yummly API

I'm trying to retrieve data from the Yummly API through Amazon Alexa using ws-yummly in Node.js deployed on AWS Lambda. I'm fairly new to all aspects of this, but new to javascript in particular (Python is my 'native' language).
Here is what I have for my recommendation intent:
"RecommendationIntent": function () {
// delegate to Alexa to collect all the required slots
let filledSlots = delegateSlotCollection.call(this);
if (!filledSlots) {
return;
}
console.log("filled slots: " + JSON.stringify(filledSlots));
// at this point, we know that all required slots are filled.
let slotValues = getSlotValues(filledSlots);
console.log(JSON.stringify(slotValues));
const mainIngredientQuery = slotValues.mainIngredient.resolved;
async function main (queryWord) {
const resp = await Yummly.query(queryWord)
.maxTotalTimeInSeconds(1400)
.maxResults(20)
.minRating(3)
.get();
const names = resp.matches.map(recipe => recipe.recipeName);
const speechOutput = String(names[0]);
this.response.speak(speechOutput);
this.emit(":responseReady");
}
main(mainIngredientQuery).catch(error => console.error(error))
},
This is in the index.js file deployed on AWS lambda. I have isolated the problem to the async function. I have tested the function locally and it returns to console.log a list of recipe names. I want to have Alexa say these names. Or at least one name.
If I put the speechOutput assignment inside (as it is now), then I get an error that the 'Speechlet Response is set to null'.
If I tell it to 'return names' and set the external assignment to names or names[0] I get object promise or undefined (respectively).
Everything else in my program works fine and test these two bits apart they work, but putting them together doesn't work. I think that this is a syntax or placement error, but I don't understand the structure or formatting well enough yet (still learning) to understand what to try next.
How about using Promise.then like this:
async function main (queryWord) {
const resp = await Yummly.query(queryWord)
.maxTotalTimeInSeconds(1400)
.maxResults(20)
.minRating(3)
.get();
const names = resp.matches.map(recipe => recipe.recipeName);
return String(names[0]);
}
main(mainIngredientQuery)
.catch( error => console.error(error) )
.then( data => {
this.response.speak( data );
this.emit(":responseReady");
});
I'm updating this in case anyone else has the same problem.
If you notice, in my original code, I had an async function inside the intent. That didnt work because the intent itself was/is a function. By making the intent function an async function instead, I was able to solve the problem.
The following is working code for an async/await Alexa Intent.
The full index.js is available on my github if you want to take a look, but that will be a more advanced final version. The code below immediately follows up on the original question.
"RecommendationIntent": async function main () {
// delegate to Alexa to collect all the required slots
let filledSlots = delegateSlotCollection.call(this);
if (!filledSlots) {
return;
}
console.log("filled slots: " + JSON.stringify(filledSlots));
// at this point, we know that all required slots are filled.
let slotValues = getSlotValues(filledSlots);
console.log(JSON.stringify(slotValues));
const mainIngredientQuery = slotValues.mainIngredient.resolved;
const resp = await Yummly.query('chicken')
.maxTotalTimeInSeconds(1400)
.maxResults(20)
.minRating(3)
.get();
const names = resp.matches.map(recipe => recipe.recipeName);
console.log(names);
const speechOutput = names[0];
this.response.speak(speechOutput);
this.emit(":responseReady");
},

NodeJS Wait till a function has completed

I have an application where i have a Client class, that needs to be executed with different configurations for each user when i receive a message on discord(It's a chat application having its own API calls for checking for new messages etc). I have initialized the obj constructor whenever i receive a new message with new configurations for that user, but the problem is when i receive multiple messages at the same time, only the latest user's configurations are used for all the client objects created. Attaching sample code from the application:
Wait for message code:
const app = require("./Client")
const app2 = require("./MyObject")
bot.on('message', async (message) => {
let msg = message.content.toUpperCase(), pg;
let client = await new app2.MyObject();
//set all such configurations
config.a = "valuea";
// initialize my
pg = await new app.Client(config, client);
let result = await pg.Main(bot, message, params).then((result) => {
// Do stuff with result here
});
});
Client class:
class Client {
constructor(config, client) {
this.config = config;
this.client = client;
}
async Main(bot, message, params) {
let result = {};
this.client.setProperty1("prop");
await this.client.doSOmething();
result = await this.doSOmethingMore(message);
this.client.doCleanUp();
return result;
}
}
I had also tried initializing the obj constructor in the Client class, but even that fails for some reason.
Any suggestions how Can i correct my code?
You don't need to use .then and await at the same time.
bot.on('message', async (message) => {
let msg = message.content.toUpperCase();
let client = await new MyObject();
//set all such configurations
config.a = "valuea";
// initialize my
pg = new app.Client(config, client);
let result = await pg.Main(bot, message, params);
// Do stuff with result here
console.log(result);
});
(You don't need to use await at constructor call because it is not an async method)
note: async-await will work on higher version of node than 7.6
If you want to use .then:
bot.on('message', (message) => {
let msg = message.content.toUpperCase();
new MyObject().then(client => {
//set all such configurations
config.a = "valuea";
// initialize my
pg = new app.Client(config, client);
pg.Main(bot, message, params).then(result => {
// Do stuff with result here
console.log(result);
});
});
});
It's difficult to be certain, since you don't include the MyObject code, but generally it's a bad practice to return a promise from a constructor.
Further, if you don't do it right, then whatever that promise resolves to might not in fact be the Client that your calling code expects.

Categories

Resources