How do I ensure my attachments are synced before adding them to an email or pdf?

I want to attach signatures, documents, or photos to an email or pdf, and I am using CloudCode to process my data. How can I ensure that the attachment has been uploaded before the CloudCode task is triggered?

See the example below:

Say your model has a field, customer_signature of type: signature. You want to ensure that before the ready webhook is fired to CC, the signature is uploaded. This is accomplished by adding the field attribute to the webhook, and setting the state to uploaded. The valid options for state are uploaded and present. If you only want to check that it has been captured for a given task, present is sufficient.

<webhook type="ready" receiver="cloudcode" action="mytask" >
   <field name="customer_signature" state="uploaded" required="true" embed="true" />
</webhook>
4 Likes

@jason’s answer is perfect if you are only waiting on attachment fields on the object that is supposed to trigger your webhook.

In some scenarios you may not be able to use this mechanism, or maybe not exclusively, because you may have multiple attachments spread across multiple different objects that need to be combined into your report, or because you may be using a separate dedicated report_generation object, linked to the main object, which holds the webhook trigger but does not have the attachments itself.

In these scenario’s you are best placed to check if the attachments have been uploaded in your CC task before attempting to use them in your report.

Below is a basic example that implements a basic check if attachment is uploaded, wait and reschedule report generation if necessary. It works with a direct webhook but also as a rescheduled task

const delay = ms => new Promise(res => setTimeout(res, ms));

export async function run(params) {
    // get the Report Request object from the webhook or the reschedule Params
    let reportRequestObject = await DB.report_request.first(params.object.id);

    // get the related parent object - this is the object that has all the report data
    let parentObject = await reportRequestObject.parentObject();

    // check if the attachment(s) on the parent object is available
    if (parentObject.attachment_field && !parentObject.attachment_field.present()) {
        console.log('Attachment captured but not yet uploaded. Waiting 10 seconds');
        await delay(10000);

        console.log('Checking if signature is now available');
        await parentObject.reload();

        if (!parentObject.attachment_field.present()) {
            // optionally you can track and limit the number of retries that you allow
            // you can also consider an exponential backup strategy, increasing the time between each subsequent rescheduling event for the same request.
            console.log('Attachment still not available, rescheduling CC task');
            let rescheduleParams = { object: { id: reportRequestObject.id } };
            try {
                await CloudCode.scheduleTask({ task: this.name, parameters: rescheduleParams });
            } catch (er) {
                try {
                    console.log('Initial rescheduling failed, trying again');
                    await CloudCode.scheduleTask({ task: this.name, parameters: rescheduleParams });
                } catch (er) {
                    console.log('FAILED');
                    throwError(er)
                }
            }
        }

      // the rest of the report generation code happens here
      ...

    }