Looking for help with writing server-side plugin

Use case: I want to consume google emails through their api for use in my app.

I already have the data. It’s currently in base64url which I think mailparser can handle. Their documentation is very sparse, so, if needed, I can decode in a pre-workflow step before sending to the mailparser action.

gmail creates rfc822 emails and base64url encodes them. I’m getting the raw body and trying to pass it to mailparser for processing. Then, send the different parts of the email (subject, body, to, from, etc.) to a table in my database through the plugin’s outputs that I’ve defined for the different parts (e.g. subject, body, to, from). One output will equal one field in the table.

Hi there @Kfawcett

Yep, I’ve done server side actions. It was painful to work out, but worth it.

I don’t have any time to share, but I created a beginner’s guide which can help you along the way…

http://forum.bubble.io/t/plugins-for-beginners-accessing-input-fields-in-plugin-action-javascript

Good luck!
Antony.

Thanks @antony. The problem I am unable to figure out is, how to pass the values from the various items back to the outputs I’ve defined in the plugin. Here is some simple code, which works in repl.it, but I’m not able to figure out how to pass the values (e.g. parsed.subject) into my plugin’s output(s) (e.g. subject).

You can test it here: https://repl.it/@kfawcett/mailparsersimpleparser

const simpleParser = require('mailparser').simpleParser;

let email = "From: 'Sender Name' <sender@example.com>\r\n"+
"To: 'Receiver Name' <receiver@example.com>\r\n"+
"Subject: Hello world!\r\n"+
"\r\n"+
"How are you today?";

simpleParser(email)
.then(parsed => {
console.log("From:", parsed.from.value);
console.log("Subject:", parsed.subject);
console.log("Text body:", parsed.text);
})
.catch(err => {
   if (err.message === 'not ready') {
   throw err;
}
});

This is the something I’ve tried in the plugin, but doesn’t work.

function(properties, context) {

const simpleParser = require('mailparser').simpleParser;

let email = properties.input;


simpleParser(email)
    .then(parsed => {
     return {
     subject: parsed.subject,
     to: parsed.to.value,
     from: parsed.from.value
     }
    })
    .catch(err => {
      if (err.message === 'not ready') {
       throw err;
    }
    });
}
1 Like

Hey @Kfawcett, thanks for making me realise I only covered inputs! I just updated my Beginner’s Guide post to cover outputs too…

I’ve posted the details below for quick reference!

Best wishes,

Antony.

Passing Values Back Out - Server Side
The plugin I have been working on has server side outputs defined as follows:

To write to these, use the code:

var my_title_out, my_email_out, my_messaging_out;
// lots of data processing...
return {"title_out":my_title_out, "email_out":my_email_out, "messaging_out":my_messaging_out, "error_out":false};

Passing Values Back Out - Element Based Client Side
I have created a similar element based client side plugin, so it has the same outputs (or “Exposed States” in this case) defined as follows:

To write to these, use the code:

var my_title_out, my_email_out, my_messaging_out;
// lots of data processing...
instance.publishState("title_out", my_title_out);
instance.publishState("email_out", my_email_out);
instance.publishState("messaging_out", my_messaging_out);
instance.publishState("error_out", false);
1 Like

Thanks @antony. That will help.

I think my issue now is more related to the function in my plugin (i.e. simpleParser) and the callback (i.e. parsed) in the function.

How do I pass the values from the callback (e.g. parsed.subject) to the plugin’s
output(s)?

I encountered exactly the same issue with another node module over the weekend. I tried a number of different “async” approaches to no avail; although I did manage to trigger several server (500 status code) errors.

I wonder if @marca, the “node meister”, might be able to shed some light. Knowing how to get such values back into Bubble would certainly open up some possibilities with SSA’s.

@bubble’s documentation is severely lacking for this. I had to enlist three different node/javascript experts, and the first two said it couldn’t be done or that Bubble’s documentation needed more details and to contact them for help.

The final person was able to figure it out after much trial and error with the below code being the end result that I hope will help others. So, I’d like to give a big recommendation to Nigel Peck on the Codementors site. If you need any help with coding, he knows his stuff. https://www.codementor.io/nigelbpeck

TLDR: You have to do a “return context.async…” with a callback.

function(properties, context) {

	var simpleParser = require('mailparser').simpleParser;

	var email = "From: 'Sender Name' <sender@example.com>\r\n"+
    "To: 'Receiver Name' <receiver@example.com>\r\n"+
    "Subject: Hello world!\r\n"+
    "\r\n"+
    "How are you today?";
    
return context.async(function(cb){
    simpleParser(email)
        .then(parsed => {
	        cb(undefined, { 
                subject: parsed.subject, 
                to: JSON.stringify(parsed.to.value), 
                from: JSON.stringify(parsed)
            });   
        })
        .catch(err => {
         
        cb(err);
        });
});
}
12 Likes

Suhhhweeet!! Thanks so much for sharing, Keith! And I thought I’d tried that! (I obviously didn’t get the right incantation.)

I’m eager to try it out! :grinning:

1 Like

Thanks! Bookmarking your post and his profile.

And here’s the same thing in async/await syntax instead of “thenable”, which I find a bit more readable…

return context.async( async callback => {
    try {
        let parsed = await simpleParser( email );
        callback( undefined, {
            subject : parsed.subject,
            to      : JSON.stringify( parsed.to.value ),
            from    : JSON.stringify( parsed.from.value )
        });
    }
    catch ( err ) {
        callback( err );
    }
});

And here’s a “template” example of using context.async(), which might be helpful to add to the docs (along with a better-worded description)…

return context.async( async callback => {
    try {
        let result = await myAsyncCall( args );
        callback( undefined, bubble_return_object );
    }
    catch ( err ) {
        callback( err );
    }
});

The undefined value is there because, per the docs, the callback expects both err and res arguments. The bubble_return_object represents the object defined in the Return values (output values) section of the plugin editor for server-side actions.

11 Likes

Is context.async blocking or non-blocking?
Edit: and why use context.async instead of just using promises or async/await? Does it forces blocking on that workflow action before proceeding to the next one?

@vini_brito, that does seem to be the case.

Here’s the next issue that I could use some help with. Is there a way to define if statements in the callback? I’m defining a bunch of outputs and sometimes a few of them may not have values, so I need/want to wrap them in if statements, but Bubble is complaining that it’s not correct syntax.

return context.async(function(cb){
    simpleParser(email)
        .then(parsed => {
	        cb(undefined, { 
                if(parsed.subject) {subject: parsed.subject}
                if(parsed.to) {to: parsed.to}
            });   
        })
        .catch(err => {
         
        cb(err);
        });
});
}

Bubble is correct. If there’s no value, do you want to specify a default or omit that property altogether?

omit it. For example, if the CC field is not populated I don’t need it.

In that case, one way to go about it would be to create an empty object and then conditionally add properties if the corresponding value is present…

        // Create empty object
        let out_obj = {};

        // Add props as needed
        if ( parsed.subject ) {
            out_obj.subject = parsed.subject;
        }
        // etc.

        cb( undefined, out_obj );

Thanks! I’ll try that tomorrow.

What I don’t know, though, is whether Bubble requires all fields defined in the plugin editor to be present in the returned object. I guess we’ll find out.

You can create a bunch of outputs and it does not require them, but if you are setting them in the code, then it appears it expects a value.

This topic was automatically closed after 70 days. New replies are no longer allowed.