UNPKG

@pankod/refine-supabase

Version:

refine Supabase data provider. refine is a React-based framework for building internal tools, rapidly. It ships with Ant Design System, an enterprise-level UI toolkit.

363 lines (310 loc) 10.5 kB
import { DataProvider, LiveProvider, CrudFilter, LiveEvent, HttpError, CrudOperators, } from "@pankod/refine-core"; import { createClient, PostgrestError, RealtimeSubscription, SupabaseClient, } from "@supabase/supabase-js"; import { SupabaseEventTypes, SupabaseRealtimePayload, } from "@supabase/supabase-js/dist/main/lib/types"; const liveTypes: Record<SupabaseEventTypes, LiveEvent["type"]> = { INSERT: "created", UPDATE: "updated", DELETE: "deleted", "*": "*", }; const supabaseTypes: Record<LiveEvent["type"], SupabaseEventTypes> = { created: "INSERT", updated: "UPDATE", deleted: "DELETE", "*": "*", }; const mapOperator = (operator: CrudOperators) => { switch (operator) { case "ne": return "neq"; case "nin": return "not.in"; case "contains": return "ilike"; case "ncontains": return "not.ilike"; case "containss": return "like"; case "ncontainss": return "not.like"; case "null": return "is"; case "nnull": return "not.is"; case "between": case "nbetween": throw Error(`Operator ${operator} is not supported`); default: return operator; } }; const generateFilter = (filter: CrudFilter, query: any) => { switch (filter.operator) { case "eq": return query.eq(filter.field, filter.value); case "ne": return query.neq(filter.field, filter.value); case "in": return query.in(filter.field, filter.value); case "gt": return query.gt(filter.field, filter.value); case "gte": return query.gte(filter.field, filter.value); case "lt": return query.lt(filter.field, filter.value); case "lte": return query.lte(filter.field, filter.value); case "contains": return query.ilike(filter.field, `%${filter.value}%`); case "containss": return query.like(filter.field, `%${filter.value}%`); case "null": return query.is(filter.field, null); case "or": const orSyntax = filter.value .map((item) => `${item.field}.${item.operator}.${item.value}`) .join(","); return query.or(orSyntax); default: return query.filter( filter.field, mapOperator(filter.operator), filter.value, ); } }; const handleError = (error: PostgrestError) => { const customError: HttpError = { ...error, message: error.message, statusCode: parseInt(error.code), }; return Promise.reject(customError); }; const dataProvider = (supabaseClient: SupabaseClient): DataProvider => { return { getList: async ({ resource, pagination, filters, sort, metaData }) => { const current = pagination?.current || 1; const pageSize = pagination?.pageSize || 10; const query = supabaseClient .from(resource) .select(metaData?.select ?? "*", { count: "exact", }) .range((current - 1) * pageSize, current * pageSize - 1); sort?.map((item) => { query.order(item.field, { ascending: item.order === "asc" }); }); filters?.map((item) => { generateFilter(item, query); }); const { data, count, error } = await query; if (error) { return handleError(error); } return { data: data || [], total: count || 0, }; }, getMany: async ({ resource, ids, metaData }) => { const { data, error } = await supabaseClient .from(resource) .select(metaData?.select ?? "*") .in(metaData?.id ?? "id", ids); if (error) { return handleError(error); } return { data: data || [], }; }, create: async ({ resource, variables }) => { const { data, error } = await supabaseClient .from(resource) .insert(variables); if (error) { return handleError(error); } return { data: (data || [])[0] as any, }; }, createMany: async ({ resource, variables }) => { const { data, error } = await supabaseClient .from(resource) .insert(variables); if (error) { return handleError(error); } return { data: data as any, }; }, update: async ({ resource, id, variables, metaData }) => { const query = supabaseClient.from(resource).update(variables); if (metaData?.id) { query.eq(metaData?.id, id); } else { query.match({ id }); } const { data, error } = await query; if (error) { return handleError(error); } return { data: (data || [])[0] as any, }; }, updateMany: async ({ resource, ids, variables, metaData }) => { const response = await Promise.all( ids.map(async (id) => { const query = supabaseClient .from(resource) .update(variables); if (metaData?.id) { query.eq(metaData?.id, id); } else { query.match({ id }); } const { data, error } = await query; if (error) { return handleError(error); } return (data || [])[0]; }), ); return { data: response, }; }, getOne: async ({ resource, id, metaData }) => { const query = supabaseClient .from(resource) .select(metaData?.select ?? "*"); if (metaData?.id) { query.eq(metaData?.id, id); } else { query.match({ id }); } const { data, error } = await query; if (error) { return handleError(error); } return { data: (data || [])[0] as any, }; }, deleteOne: async ({ resource, id, metaData }) => { const query = supabaseClient.from(resource).delete(); if (metaData?.id) { query.eq(metaData?.id, id); } else { query.match({ id }); } const { data, error } = await query; if (error) { return handleError(error); } return { data: (data || [])[0] as any, }; }, deleteMany: async ({ resource, ids, metaData }) => { const response = await Promise.all( ids.map(async (id) => { const query = supabaseClient.from(resource).delete(); if (metaData?.id) { query.eq(metaData?.id, id); } else { query.match({ id }); } const { data, error } = await query; if (error) { return handleError(error); } return (data || [])[0]; }), ); return { data: response, }; }, getApiUrl: () => { throw Error("Not implemented on refine-supabase data provider."); }, custom: () => { throw Error("Not implemented on refine-supabase data provider."); }, }; }; const liveProvider = (supabaseClient: SupabaseClient): LiveProvider => { return { subscribe: ({ channel, types, params, callback, }): RealtimeSubscription => { const resource = channel.replace("resources/", ""); const listener = (payload: SupabaseRealtimePayload<any>) => { if ( types.includes("*") || types.includes(liveTypes[payload.eventType]) ) { if ( liveTypes[payload.eventType] !== "created" && params?.ids !== undefined && payload.new?.id !== undefined ) { if ( params.ids .map(String) .includes(payload.new.id.toString()) ) { callback({ channel, type: liveTypes[payload.eventType], date: new Date(payload.commit_timestamp), payload: payload.new, }); } } else { callback({ channel, type: liveTypes[payload.eventType], date: new Date(payload.commit_timestamp), payload: payload.new, }); } } }; const client = supabaseClient .from(resource) .on(supabaseTypes[types[0]], listener); types .slice(1) .map((item) => client.on(supabaseTypes[item], listener)); return client.subscribe(); }, unsubscribe: async (subscription: RealtimeSubscription) => { supabaseClient.removeSubscription(subscription); }, }; }; export { dataProvider, liveProvider, createClient };