Edits history of script submission #22562 for ' Upsert data (supabase)'

  • bun
    One script reply has been approved by the moderators
    Ap­pro­ved
    import { createClient, SupabaseClient } from "@supabase/[email protected]"
    
    /**
     * @param on_conflict Comma-separated UNIQUE column(s) to specify how duplicate
     * rows are determined. Two rows are duplicates if all the onConflict columns are equal.
     *
     * @param ignore_duplicates If true, duplicate rows are ignored. If false, duplicate
     * rows are merged with existing rows.
     *
     * @param token Supabase `access_token` and `refresh_token`. `expires_at` (optional) is a UNIX
     * timestamp in seconds.
     *
     * @param count Count algorithm to use to count rows in the table or view.
     * `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the hood.
     * `"planned"`: Approximated but fast count algorithm. Uses the Postgres statistics under the hood.
     * `"estimated"`: Uses exact count for low numbers and planned count for high numbers.
     */
    type Supabase = {
      url: string;
      key: string;
    };
    export async function main(
      auth: Supabase,
      table: string,
      values: any,
      on_conflict?: string,
      ignore_duplicates: boolean = true,
      return_updated: boolean = false,
      token?: {
        access: string;
        refresh: string;
        expires_at?: number;
      },
      count?: "exact" | "planned" | "estimated",
    ) {
      return await refreshAndRetryIfExpired(auth, token, async (client) => {
        let query: any = client
          .from(table)
          .upsert(
            values,
            removeObjectEmptyFields({
              onConflict: on_conflict,
              ignoreDuplicates: ignore_duplicates,
              count,
            }),
          );
        if (return_updated) {
          query = query.select();
        }
    
        return query;
      });
    }
    
    function removeObjectEmptyFields(
      object?: Record<string, any>,
      removeEmptyArraysAndObjects = true,
      createNewObject = true,
    ) {
      if (!object || typeof object !== "object") return {}
      const obj = createNewObject ? { ...object } : object
      const emptyValues = [undefined, null, ""]
      for (const key in obj) {
        const value = obj[key]
        if (emptyValues.includes(value)) {
          delete obj[key]
        } else if (typeof value === "object") {
          if (Object.keys(value).length) {
            obj[key] = removeObjectEmptyFields(value, removeEmptyArraysAndObjects, false)
          }
          if (!Object.keys(value).length && removeEmptyArraysAndObjects) {
            delete obj[key]
          }
        }
      }
      return obj
    }
    
    async function refreshAndRetryIfExpired(
      auth: { url: string; key: string },
      token: { access: string; refresh: string; expires_at?: number } | undefined,
      fn: (client: SupabaseClient) => Promise<{ data: any; error?: any }>,
    ): Promise<{ data: any; error?: any; token?: { access: string; refresh: string; expires_at?: number } }> {
      const makeClient = async (autoRefreshToken: boolean) => {
        const client = createClient(auth.url, auth.key, {
          auth: { autoRefreshToken, persistSession: false },
        })
        if (token) {
          await client.auth.setSession({ access_token: token.access, refresh_token: token.refresh })
        }
        return client
      }
      try {
        let result = await fn(await makeClient(false))
        if (result?.error?.code === "PGRST301" && token) {
          const client = await makeClient(true)
          result = await fn(client)
          const { data } = await client.auth.getSession()
          if (data?.session) {
            token = {
              access: data.session.access_token,
              refresh: data.session.refresh_token,
              expires_at: data.session.expires_at,
            }
          }
        }
        return { ...result, token }
      } catch (error) {
        return { data: null, error }
      }
    }
    

    Submitted by hugo989 7 days ago