firebase / extensions

Source code for official Firebase extensions
https://firebase.google.com/products/extensions
Apache License 2.0
882 stars 372 forks source link

[firestore-send-email] Unable to send base64 encoded attachment #981

Closed atanaskanchev closed 2 years ago

atanaskanchev commented 2 years ago

[READ] Step 1: Are you in the right place?

Issues filed here should be about bugs for a specific extension in this repository. If you have a general question, need help debugging, or fall into some other category use one of these other channels:

[REQUIRED] Step 2: Describe your configuration

 // ? send the email notification
       return await db.collection('mail').add({
         to:email,
         template: {
           name: 'template',
           data: {
             ...
           },
           attachments: [
             {
               filename: `Invoice-${invoice.invoice_number}.pdf`,
               content: pdf.toString('base64'),
               encoding: 'base64',
               contentType: 'application/pdf'
             }
           ]
         }
       })
DEFAULT_FROM=support@_.com
DEFAULT_REPLY_TO=support@_.com
LOCATION=us-central1
MAIL_COLLECTION=mail
SMTP_CONNECTION_URI=smtps://email_address@smtp.gmail.com:465
SMTP_PASSWORD=password
TEMPLATES_COLLECTION=mail_templates
USERS_COLLECTION=users

[REQUIRED] Step 3: Describe the problem

The extension does not process a base64 encoded attachment. The email is successfully send, however, the email does not have a file attachment.

Steps to reproduce:

Create a record in the mail collection

image

Expected result

The email should have a PDF attachment.

Actual result

There is no attachment in the received email.

dackers86 commented 2 years ago

Hi @atanaskanchev.

Could you possibly be missing a content type definition?

export interface Attachment {
  filename?: string;
  content?: string;
  path?: string;
  encoding?: string;
  raw?: string;
  href?: string;
  httpHeaders?: any;
  contentDisposition?: string;
  contentType?: string;
  headers?: any;
  cid?: string;
}
atanaskanchev commented 2 years ago

Hi @atanaskanchev.

Could you possibly be missing a content type definition?

export interface Attachment {
  filename?: string;
  content?: string;
  path?: string;
  encoding?: string;
  raw?: string;
  href?: string;
  httpHeaders?: any;
  contentDisposition?: string;
  contentType?: string;
  headers?: any;
  cid?: string;
}

Hi @dackers86 the contentType is set to application/pdf

image

atanaskanchev commented 2 years ago

I've just notice some strange warning in the execution logs:

{"location":"us-central1","mailCollection":"mail","smtpConnectionUri":"<omitted>","smtpPassword":"<omitted>","defaultFrom":"xxx","defaultReplyTo":"xxx","usersCollection":"users","templatesCollection":"mail_templates","testing":false,"severity":"INFO","message":"Started execution of extension with configuration"} {"severity":"INFO","message":"Completed execution of extension"}

{"severity":"WARNING","message":"message 'undefined' is not a valid object - please add as an object or firestore map, otherwise you may experience unexpected results."}

dackers86 commented 2 years ago

That warning message should be ok as it is just stating a message object has not been included.

This may be an edge case, but the attachments are expected to be included in the template that you have included as part of your document.

This should be accessed through a name and any relevant data. For example:


 template?: {
    name: string;
    data?: { [key: string]: any };
  };
``
atanaskanchev commented 2 years ago

The template looks like

image

and it is set in the mail doc as image

dackers86 commented 2 years ago

@atanaskanchev

Below is a basic example of how to add a base64 attachment through a template:

/** create an example template */
await firestore()
.collection("templates")
.doc("base64_example")
.set({
  html: "testing",
  subject: "testing subject",
  attachments: [
    {
      filename: `ImportantDocument.pdf`,
      content: "THISISAB64STRING",
      encoding: "base64",
    },
  ],
});
/** send an email using the new template*/
await firestore()
.collection("mails")
.add({
  to: ["darren@invertase.io"],
  template: {
    name: "base64_example",
    data: {},
  },
});

Is this the type of solution you are looking for?

atanaskanchev commented 2 years ago

Hi @dackers86 thanks for the suggestion, but my goal is to use the template doc to set the html body and dynamically generate one or more base64 attachments. Adding the attachments to the template doc defeats its purpose as a template. I suppose can generate a new template per email, but ideally the template should not handle the attachments

dackers86 commented 2 years ago

@atanaskanchev I think I understand, does this example meet your requirements?

  await firestore().collection("templates").doc("base64_example").set({
    html: "{{html_data}}",
    subject: "{{subject_data}}",
  });

  await firestore()
    .collection("mail")
    .add({
      to: ["darren@invertase.io"],
      message: {
        attachments: [
          {
            filename: `ImportantDocument.pdf`,
            content: "",
            encoding: "base64",
          },
        ],
      },
      template: {
        name: "base64_example",
        data: { html_data: "This is html", subject_data: "This is a subject" },
      },
    });
atanaskanchev commented 2 years ago

@dackers86 this works perfectly, thanks for your help!

cjbland commented 4 months ago

This should really be in the documentation, it was driving me crazy until I found this issue.