Skip to main content
Question

Custom Gmail App Authentication Issue


Todd Harper
Forum|alt.badge.img+8

I’ve built a custom (private) Gmail integration for a client in the Zapier Platform to use some API actions not included in the standard Gmail integration - checking whether a label exists for a given year, creating it if it doesn’t, adding a child label to the year (to represent a particular event), and then creating a filter so that emails from a given client are always assigned that label.

Everything appears to be set up correctly.

OAuth test authentication in Zapier platform works like a charm:

 

Testing the action’s API request (which does all of the above in one action step) works as expected:

 

Testing the action in the Zapier editor works as expected:

 

But for some reason, live runs fail with the following error message:

 

 

Any ideas on what’s going on here? For those who would like to see it, I’ve included the code for the step below:

// Function to get a list of all existing labels from Gmail account
const getLabels = () => {
  const options = {
    url: `https://gmail.googleapis.com/gmail/v1/users/me/labels`,
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': `Bearer ${bundle.authData.access_token}`
    }
  };

  return z.request(options).then((response) => {
    response.throwForStatus();
    return response.json;
  });
};

// Function to create a new label
const createLabel = (labelName) => {
  const options = {
    url: `https://gmail.googleapis.com/gmail/v1/users/me/labels`,
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': `Bearer ${bundle.authData.access_token}`
    },
    body: {
      "name": labelName
    }
  };

  return z.request(options).then((response) => {
    response.throwForStatus();
    return response.json;
  });
}

// Helper function to format date as M.DD.YY
const formatDateMDDYY = (date) => {
  const eventMonth = (date.getMonth() + 1).toString();
  const eventDay = date.getDate().toString().padStart(2, '0');
  const eventYear = date.getFullYear().toString().slice(-2);
  return `${eventMonth}.${eventDay}.${eventYear}`;
};

// Function to ensure a label exists for the given year and to create a child event label
const ensureYearLabelAndCreateEventLabel = (labels) => {
  const eventDate = new Date(bundle.inputData.event_date);
  const year = eventDate.getFullYear();
  const yearLabel = `Clients/${year}`;

  const existingLabel = labels.labels.find(label => label.name === yearLabel);

  const labelPromise = existingLabel ? Promise.resolve(existingLabel) : createLabel(yearLabel);

  return labelPromise.then(targetLabel => {
    const formattedDate = formatDateMDDYY(eventDate);
    const labelName = `${targetLabel.name}/${formattedDate} - ${bundle.inputData.event_name}`;
    return createLabel(labelName);
  });
}

// Function to get a list of all existing labels from Gmail account
const getFilters = () => {
  const options = {
    url: 'https://gmail.googleapis.com/gmail/v1/users/me/settings/filters',
    method: 'GET',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${bundle.authData.access_token}`
    }
  };

  return z.request(options).then(response => {
    if (response.json) {
      response.throwForStatus();
      return response.json;
    }
    return {filter: false};
  });
};

// Function to find and delete filters matching a given email address
const findAndDeleteFilter = (filters, emailAddress) => {
  if (emailAddress) {
    const targetFilter = filters.find(filter => {
      if (!filter.criteria.from) {
        return false;
      }
      const emailAddresses = filter.criteria.from.split(/\sOR\s/i);
      return emailAddresses.includes(emailAddress);
    });

    if (!targetFilter) {
      return;
    }

    const options = {
      url: `https://gmail.googleapis.com/gmail/v1/users/me/settings/filters/${targetFilter.id}`,
      method: 'DELETE',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${bundle.authData.access_token}`
      }
    }

    return z.request(options);
  }
}

// Function to check whether any filters exist for either the primary or secondary email address and deletes theme
const findAndDeleteFilters = async (filterObject) => {
  if (filterObject.filter) {
    const filters = filterObject.filter;
    await Promise.all([
      findAndDeleteFilter(filters, bundle.inputData.primary_email),
      findAndDeleteFilter(filters, bundle.inputData.secondary_email)
    ]);
    return { message: "Filters deleted." };
  }
  return { message: "No filters were found." };
};

// Function to create a new filter and assign it to given label
const createFilter = (label) => {
  let primary = bundle.inputData.primary_email;
  let secondary = bundle.inputData.secondary_email;
  let emailAddresses = primary;

  if (primary && secondary) {
    emailAddresses = `${primary} OR ${secondary}`;
  }

  const options = {
    url: `https://gmail.googleapis.com/gmail/v1/users/me/settings/filters`,
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': `Bearer ${bundle.authData.access_token}`
    },
    body: {
      "action": {
        "addLabelIds": [label.id]
      },
      "criteria": {
        "from": emailAddresses
      }
    }
  };

  return z.request(options).then((response) => {
    response.throwForStatus();
    return response.json;
  });
}

// Main execution flow
return getLabels()
  .then(ensureYearLabelAndCreateEventLabel)
  .then(createdLabel => getFilters().then(filters => ({filters, createdLabel})))
  .then(({filters, createdLabel}) => findAndDeleteFilters(filters).then(() => createdLabel))
  .then(createFilter)
  .catch(error => {
    // Log more detailed information about the error
    console.error("Error Details:", error);
    return {
      message: "There was an error.",
      errorDetails: error.message
    }
  });

 

Did this topic help you find an answer to your question?

0 replies

Be the first to reply!