Wierd behaviour in firebase storage - javascript

I have a csv file in storage(firebase-admin), and In my cloud function tried to create a readable stream in it
my code is something like this.
const customErrObject = {any: false, messag: []};
storageFile.createReadStream().pipe(fastCsv({headers: false,})
.on("data", async data => {
try {
// retrieve a user here from `firestore`
console.log('user.empty', user.empty);
// more codes
// retrieve kyc data
console.log('kyc.empty', kyc.empty);
// create notification
} catch (e) {
customErrObject.any = true;
customErrObject.message.push(e);
}
}
.on("end", () => {
console.log('customErrObject on end', customErrObject);
if(customerErrObject.any) //return 400 with error messages
else // return 200
})
now during runtime i would expect it to follow like this
user.empty false
kyc.empty false
customerErrObject on end, object log here....
but to my surprise this showed in the logs
user.empty false
customerErrorObject on end, object log here...
kyc.empty false
it really confused me as to why it didn't follow my expected sequence.
any help would be great!

Related

Node.js Returning a value from an async function - Electron

I am creating a project where I need to take a user input - pass it through a function and return the new value to the user - seems simple enough. I am new to async functions and have read everything I possibly can, and can't works out if there's a more fundamental issue I am missing. I will show the basic code, and then what I wish to achieve. I believe the issue, is that I am returning back the status of the function rather than the value, but just can't work it out.
Basic Code:
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
generateResponse('james', 'hello world'); // Takes a user's name & input and recieves a response from a python file.
event.reply('textRecieve', 'hello world'); // Sends 'Hello World' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = "";
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
return message; // attempting to return the 'Message' from the python file
});
} catch (error) {
console.log("You've f*cked it somewhere my friend");
console.log(error);
}
}
Python Script:
import sys
name = sys.argv[1]
text = sys.argv[2]
print(f'Python File: {name} Text: {text}')
sys.stdout.flush()
Returns: (as expected)
> Executing task: npm run start <
> electron-quick-start#1.0.0 start
> electron .
Python File: james Text: hello world
What I'd Like it to do:
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
message = generateResponse('james', 'hello world'); // Takes a user's name & input and recieves a response from a python file, retunring the message to the 'message' variable.
console.log(message);
event.reply('textRecieve', 'message would send here'); // Sends the 'Message' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = ""
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
return message; // attempting to return the 'Message' from the python file
});
} catch (error) {
console.log("You've f*cked it somewhere my friend")
console.log(error)
}
return content; // content needs to be message instead due to async nature it returns empty string
}
Returns:
> Executing task: npm run start <
> electron-quick-start#1.0.0 start
> electron .
Promise { '' }
Python File: james Text: hello world
TLDR; I would like to take the 'message' generated through 'generateResponse()' and pass it through to my 'event.reply()'. Instead, I am receiving what I believe to be the status of the Promise. Any help would be greatly appreciated. Thanks
You should resolve the promise first.
ipcMain.on('gpt3', (event, args) => {
async function gpt3(args) {
const message = await generateResponse('james', 'hello world');
console.log(message);
event.reply('textRecieve', 'message would send here'); // Sends the 'Message' to the user (ipcRenderer 'textRecieve')
}
gpt3(args);
})
async function generateResponse(name, text) {
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: [name, text]});
let content = ""
try {
testshell.on('message', function (message) {
console.log(message); // prints the output from the python file 'Python File: james Text: hello world'
content = message;
});
} catch (error) {
console.log("You've f*cked it somewhere my friend")
console.log(error)
}
return content; // content needs to be message instead due to async nature it returns empty string
}
Okay, so there were a few problems here... but the main was node.js 'non-ability' to pass variables around when 'asynchronous'. with node.js being new to me, I can't lie and say I was confused. Hopefully, the following link to a great workaround/method and my working code will be able to help someone:
https://stackoverflow.com/a/23667087/10246221
Code:
ipcMain - nested within app.whenReady().
ipcMain.on('gpt3', (event, input) => {
gpt3Async(event, input, function(result) {
event.reply('textRecieve', result);
console.log('gpt3Async: '+ result);
})
})
Code:
Generic 'nested' Function - free-floating around 'main.js' or 'index.js'.
function gpt3Async(event, input, callback) {
console.log('input: ' + input)
let testshell = new PythonShell('./python/text_echo.py', { mode: 'text', args: ['elliott' ,input]});
testshell.on('message', function (message) {
callback(message);
});
}
Code: Python Script 'text_echo.py' - in my case within a 'python' subdirectory.
import sys
name = sys.argv[1]
text = sys.argv[2]
print(f'Python File: {name} Text: {text}')
#sys.stdout.flush()
sys.stdout.flush()
For anyone working on a project where you need input and output for python scripts, this will help you out. also make sure you turn on the following:
webPreferences: {
//preload: path.join(__dirname, 'preload.js'),
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
sandbox: false,
},
BUT!, please be aware of the security implications this will have on your code, More info is available here: https://stackoverflow.com/a/57507392 & https://electronjs.org/docs/tutorial/security#3-enable-context-isolation-for-remote-content & much more so do some reading if this is an important project...
Okay, An explainer, or at least something that blew my mind as a beginner... . The way I finally understood it was through the example link:
https://stackoverflow.com/a/23667087/10246221
for some reason, it hadn't clicked with me that functions could be nested within functions like this, all in one line. For someone who is used to JS or node.js this may seem fundamental, but seeing as this is a first-time project to me, and maybe others - if still using python code. Hopefully, this may help!
ipcMain.on('gpt3', (event, input) => { gpt3Async(event, input, function(result) { event.reply('textRecieve', result); console.log('gpt3Async: '+ result);})})

Evaporate JS - add is undefined

I can't seem to figure out what the problem is. I'm trying to use EvaporateJS to upload files to S3, I'm also using React. Here is what my code looks like:
Blockquote
useEffect(() => {
Evaporate.create({
aws_key: AWS_ACCESS_KEY,
bucket: S3_BUCKET,
awsRegion: 'us-west-1', // s3 region
signerUrl: '/api/videos/signv4_auth',
awsSignatureVersion: '4',
computeContentMd5: true,
cloudfront: true,
cryptoMd5Method: (data) => {
return AWS.util.crypto.md5(data, 'base64');
},
cryptoHexEncodedHash256: (data) => {
return AWS.util.crypto.sha256(data, 'hex');
}
}).then(evaporate => {
console.log(evaporate);
// evaporate.add(); // showing as not a function
});
}, []);
But I get an error message: evaporate.add is not a function. When I inspect the evaporate variable that's being passed with then, it doesn't contain the add function, nor some of the other functions mentioned in documentation. Not sure why it's not working, any help would be highly appreciated.
Console output of evaporate
Error Message

Loopback is not responding with modified data after 'loaded' hook

I have the following code in my Model.js file.
Model.observe('loaded', (ctx, next) => {
const {
data,
options: {
user
}
} = ctx;
const owner = (user && data && user.uid === data.userId) || false;
console.log(
`${data.id}: loaded - access by ${user && user.name}, owner:${owner}`
);
if (!owner) {
delete data.testProp1;
}
console.log('returning: ', ctx.data);
next();
});
When I make a request, I see the following log output (server logs):
f3f9ffd6-14dc-42e5-94ba-503aa3426faa: loaded - access by User1, owner:false
returning:
{
testProp2: true,
id: 'f3f9ffd6-14dc-42e5-94ba-503aa3426faa',
userId: 'sfeywkKSuBTlf0DwE4ZOFd8RX5E3'
}
But then in the actual response the browser receives I actually get:
{
testProp1: true,
testProp2: true,
id: 'f3f9ffd6-14dc-42e5-94ba-503aa3426faa',
userId: 'sfeywkKSuBTlf0DwE4ZOFd8RX5E3'
}
Is there something in the documentation I am missing? Deleting the property is exactly what it shows in the Loopback docs here. Also, I actually see the modified data as the data property on the ctx object before calling next(). Anyone run into this issue or know some caveat to the docs that isn't explicitly stated?

Firebase: snap.child(...).once is not a function

I want to listen to some data from my firebase but I first have to check if the keys leading to the specific data has already been made. If they have not yet been made there is no problem I don't have to listen.
I get this error message:
snip.child(...).once is not a function
I have tried to just followed their documentation from here it really seems as if what I am doing should be legal.
The .hasChild(...) works fine.
rootRef.child('match-feed-comments').once('value', snip => {
if (snip.hasChild(this.props.matchId)) { // Checking if child exists
snip.child(this.props.matchId).once('value', snup => { // if the child exists we listen
if (snup.hasChild(this.props.feedComponentId)) {
snup.child(this.props.feedComponentId).on('value', snap => {
// finally I want to do stuff with the data
this.setState({
comments: snap.val()
})
});
}
});
}
});

Smooch: How to do postback dependent state transition?

I am trying to transition the script from one state to another based on Smooch postback payloads; but getting error code H12.
Consider the example https://github.com/smooch/smooch-bot-example
Say I modify the script https://github.com/smooch/smooch-bot-example/blob/master/script.js as follows
start: {
receive: (bot) => {
return bot.say('Hi! I\'m Smooch Bot! Continue? %[Yes](postback:askName) %[No](postback:bye) );
}
},
bye: {
prompt: (bot) => bot.say('Pleasure meeting you'),
receive: () => 'processing'
},
The intention is that the's bot's state would transition depending on the postback payload.
Question is, how do I make that happen?
My approach was add
stateMachine.setState(postback.action.payload)
to the handlePostback method of github.com/smooch/smooch-bot-example/blob/master/heroku/index.js
However, that threw an error code H12. I also experimented with
stateMachine.transition(postback.action,postback.action.payload)
to no avail.
I got the same issue with the [object Object] instead of a string. This is because the state you get or set with a function is contained in an object, not a string... I fixed it with this code inside index.js, replacing the existing handlePostback function in the smooch-bot-example GitHub repo:
function handlePostback(req, res) {
const stateMachine = new StateMachine({
script,
bot: createBot(req.body.appUser)
});
const postback = req.body.postbacks[0];
if (!postback || !postback.action) {
res.end();
};
const smoochPayload = postback.action.payload;
// Change conversation state according to postback clicked
switch (smoochPayload) {
case "POSTBACK-PAYLOAD":
Promise.all([
stateMachine.bot.releaseLock(),
stateMachine.setState(smoochPayload), // set new state
stateMachine.prompt(smoochPayload) // call state prompt() if any
]);
res.end();
break;
default:
stateMachine.bot.say("POSTBACK ISN'T RECOGNIZED") // for testing purposes
.then(() => res.end());
};
}
Then inside script.js all you need to do is define states corresponding to the exact postback payloads. If you have multiple postbacks that should take the user to other states, just add them to the case list like so :
case "POSTBACK-PAYLOAD-1":
case "POSTBACK-PAYLOAD-2":
case "POSTBACK-PAYLOAD-3":
case "POSTBACK-PAYLOAD-4":
Promise.all([
stateMachine.bot.releaseLock(),
stateMachine.setState(smoochPayload), // set new state
stateMachine.prompt(smoochPayload) // call state prompt() if any
]);
res.end();
break;
Note that you should not write break; at the end of each case if the outcome you want is the same (here : setting the state and prompting the corresponding message).
If you want to handle other postbacks differently, you can add cases after the break; statement and do other stuff instead.
Hope this helps!
Postbacks won't automatically transition your conversation from one state to the next, you have to write that logic yourself. Luckily the smooch-bot-example you're using already has a postback handler defined here:
https://github.com/smooch/smooch-bot-example/blob/30d2fc6/heroku/index.js#L115
So whatever transition logic you want should go in there. You can do this by creating a stateMachine and calling receiveMessage() on it the same way handleMessages() already works. For example:
const stateMachine = new StateMachine({
script,
bot: createBot(req.body.appUser)
});
stateMachine.receiveMessage({
text: 'whatever your script expects'
})
Alternatively, you could have your handlePostback implementation call stateMachine.setState(state) and stateMachine.prompt(state) independently, if you wanted to have your postbacks behave differently from regular text responses.
If you want to advance the conversation based on a postback you'll have to first output the buttons from the bot's prompt (so you can handle the button click in the receive), modify the handlePostback function in index.js, then handle the user's "reply" in your receive method - try this - modify script.js like so:
start: {
prompt: (bot) => bot.say(`Hi! I'm Smooch Bot! Continue? %[Yes](postback:askName) %[No](postback:bye)`),
receive: (bot, message) => {
switch(message.text) {
case 'Yes':
return bot.say(`Ok, great!`)
.then(() => 'hi')
break;
case 'No':
return bot.say(`Ok, no prob!`)
.then(() => 'bye')
break;
default:
return bot.say(`hmm...`)
.then(() => 'processing')
break;
}
}
},
hi: {
prompt: (bot) => bot.say('Pleasure meeting you'),
receive: () => 'processing'
},
bye: {
prompt: (bot) => bot.say('Pleasure meeting you'),
receive: () => 'processing'
},
Then modify the handlePostback function in index.js so that it treats a postback like a regular message:
function handlePostback(req, res) {
const postback = req.body.postbacks[0];
if (!postback || !postback.action)
res.end();
const stateMachine = new StateMachine({
script,
bot: createBot(req.body.appUser)
});
const msg = postback;
// if you want the payload instead just do msg.action.paylod
msg.text = msg.action.text;
stateMachine.receiveMessage(msg)
.then(() => res.end())
.catch((err) => {
console.error('SmoochBot error:', err);
res.end();
});
}
Now when a user clicks your button it will be pushed to the stateMachine and handled like a reply.

Categories