Edits history of script submission #22402 for ' Suspend/resume a flow by sending approval URL via email (gmail)'

  • bunnative
    One script reply has been approved by the moderators
    Ap­pro­ved
    //native
    
    import { getResumeUrls } from "windmill-client@1";
    
    type Gmail = {
      token: string;
    };
    export async function main(
      gmail_auth: Gmail,
      approver_emails: string[],
      subject = "Resume Windmill flow",
    ) {
      throwOnInvalidApprovers(approver_emails);
    
      const token = gmail_auth["token"];
      const email_promises = approver_emails.map((to_email) =>
        sendEmail(token, to_email, subject),
      );
      const results = await Promise.all(email_promises);
    
      return results;
    }
    
    async function sendEmail(
      gmail_token: string,
      to_email: string,
      subject: string,
    ): Promise<object> {
      const email = await getEmailBody(to_email, subject);
      const SEND_EMAIL_URL = `https://gmail.googleapis.com/gmail/v1/users/me/messages/send`;
      const body = {
        raw: email,
      };
      const response = await fetch(SEND_EMAIL_URL, {
        method: "POST",
        body: JSON.stringify(body),
        headers: {
          Authorization: `Bearer ${gmail_token}`,
        },
      });
      const response_json = await response.json();
    
      return handleSendEmailResult(response_json, to_email);
    }
    
    async function getEmailBody(to_email: string, subject: string) {
      const { approvalPage } = await getResumeUrls(to_email);
      const message = `There is a Windmill flow at ${Bun.env[
        "WM_FLOW_PATH"
      ]} run by ${Bun.env["WM_USERNAME"]} waiting for your approval to resume.
    
    In order to resume or cancel the flow go to ${approvalPage}`;
      const email_body = `From: <me>\nTo: <${to_email}>\nSubject: ${subject}\n\r ${message}`;
    
      return base64UrlEncode(email_body);
    }
    
    function throwOnInvalidApprovers(approvers: string[]) {
      if (!Array.isArray(approvers) || approvers.length === 0) {
        throw new Error("Expected at least one approver email");
      }
    }
    
    async function handleSendEmailResult(result: object, to_email: string) {
      if (Object.keys(result).includes("error")) {
        return Promise.reject({ wm_to_email: to_email, ...result });
      }
    
      return result;
    }
    
    function base64UrlEncode(data: string | Uint8Array) {
      const bytes =
        typeof data === "string" ? new TextEncoder().encode(data) : data;
      let binary = "";
      for (let i = 0; i < bytes.length; i++) {
        binary += String.fromCharCode(bytes[i]);
      }
      return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
    }
    

    Submitted by hugo989 7 days ago