From 00a23cd54b3a9e9e143414cd5d19f247f3721002 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Mon, 22 Jan 2024 18:46:52 +0100 Subject: [PATCH 1/4] Update to latest ra-data-postgrest --- .../demo/src/contacts/ContactListFilter.tsx | 18 +++++++++++++++- packages/ra-supabase-core/package.json | 4 ++-- packages/ra-supabase-core/src/dataProvider.ts | 21 +++++++++++++------ yarn.lock | 17 +++++++++++---- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/packages/demo/src/contacts/ContactListFilter.tsx b/packages/demo/src/contacts/ContactListFilter.tsx index 1976af4..44ba16e 100644 --- a/packages/demo/src/contacts/ContactListFilter.tsx +++ b/packages/demo/src/contacts/ContactListFilter.tsx @@ -12,7 +12,13 @@ import AccessTimeIcon from '@mui/icons-material/AccessTime'; import TrendingUpIcon from '@mui/icons-material/TrendingUp'; import LocalOfferIcon from '@mui/icons-material/LocalOffer'; import SupervisorAccountIcon from '@mui/icons-material/SupervisorAccount'; -import { endOfYesterday, startOfWeek, startOfMonth, subMonths } from 'date-fns'; +import { + endOfYesterday, + startOfWeek, + endOfWeek, + startOfMonth, + subMonths, +} from 'date-fns'; import { Status } from '../misc/Status'; @@ -60,6 +66,16 @@ export const ContactListFilter = () => { 'last_seen@lte': startOfMonth(new Date()).toISOString(), }} /> + - postgrestRestProvider( - `${instanceUrl}/rest/v1`, - supabaseHttpClient({ apiKey, supabaseClient }) - ); +}): DataProvider => { + const config: IDataProviderConfig = { + apiUrl: `${instanceUrl}/rest/v1`, + httpClient: supabaseHttpClient({ apiKey, supabaseClient }), + defaultListOp: 'eq', + primaryKeys: defaultPrimaryKeys, + schema: defaultSchema, + }; + return postgrestRestProvider(config); +}; /** * A function that returns a httpClient for Supabase. It handles the authentication. diff --git a/yarn.lock b/yarn.lock index 32f60de..aaab649 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2344,10 +2344,12 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== -"@raphiniert/ra-data-postgrest@2.0.0-alpha.0": - version "2.0.0-alpha.0" - resolved "https://registry.yarnpkg.com/@raphiniert/ra-data-postgrest/-/ra-data-postgrest-2.0.0-alpha.0.tgz#12bc23ba9e25388b9b630e5400d290c4b23fe1a3" - integrity sha512-9bwWARORJX6ivdCkTyt/9hQ1we4hz0pW6MiPLqK7DWLSlqWyuC7A+/U0Iix5Qd52qoiB4ppA9Xov+btrDWgLWw== +"@raphiniert/ra-data-postgrest@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@raphiniert/ra-data-postgrest/-/ra-data-postgrest-2.0.0.tgz#4038621b4fe156095dde066353e29396e33cbb39" + integrity sha512-1Ss+/xAxL62aGmCBSSJnlFMMSl0T2/bNSfeV0lplm7N9AtsOxSfySDKnCQs53ngbSBK1rPbk4ypc/mDVm4y4xQ== + dependencies: + qs "^6.11.1" "@react-spring/animated@~9.4.5": version "9.4.5" @@ -9503,6 +9505,13 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== +qs@^6.11.1: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + qs@~6.10.3: version "6.10.5" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" From 147630d86614818e8edcbac34b0d93e562ebab8f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Wed, 24 Jan 2024 11:38:31 +0100 Subject: [PATCH 2/4] support dataProvider config override and update doc --- packages/ra-supabase-core/README.md | 25 ++++++++++++++++++- packages/ra-supabase-core/src/dataProvider.ts | 17 +++++++++---- packages/ra-supabase/README.md | 25 ++++++++++++++++++- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/packages/ra-supabase-core/README.md b/packages/ra-supabase-core/README.md index fe299dd..71b8bc0 100644 --- a/packages/ra-supabase-core/README.md +++ b/packages/ra-supabase-core/README.md @@ -68,7 +68,7 @@ export const MyAdmin = () => ( ### DataProvider -`ra-supabase` is built on [`ra-data-postgrest`](https://github.com/raphiniert-com/ra-data-postgrest/tree/v2.0.0-alpha.0) that leverages [PostgREST](https://postgrest.org/en/stable/). As such, you have access the following features: +`ra-supabase` is built on [`ra-data-postgrest`](https://github.com/raphiniert-com/ra-data-postgrest/tree/v2.0.0) that leverages [PostgREST](https://postgrest.org/en/stable/). As such, you have access the following features: #### Filters operators @@ -93,6 +93,29 @@ See the [PostgREST documentation](https://postgrest.org/en/stable/api.html#opera As users authenticate through supabase, you can leverage [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security). Users identity will be propagated through the dataProvider if you provided the public API (anon) key. Keep in mind that passing the `service_role` key will bypass Row Level Security. This is not recommended. +#### Customizing the dataProvider + +`supabaseDataProvider` also accepts the same options as the `ra-data-postgrest` dataProvider (except `apiUrl`), like [`primaryKeys`](https://github.com/raphiniert-com/ra-data-postgrest/blob/master/README.md#compound-primary-keys) or [`schema`](https://github.com/raphiniert-com/ra-data-postgrest/blob/master/README.md#custom-schema). + +```jsx +// in dataProvider.js +import { supabaseDataProvider } from 'ra-supabase-core'; +import { supabaseClient } from './supabase'; + +export const dataProvider = supabaseDataProvider({ + instanceUrl: 'YOUR_SUPABASE_URL', + apiKey: 'YOUR_SUPABASE_ANON_KEY', + supabaseClient, + primaryKeys: new Map([ + ['some_table', ['custom_id']], + ['another_table', ['first_column', 'second_column']], + ]), + schema: () => localStorage.getItem("schema") || "api", +}); +``` + +See the [`ra-data-postgrest`` documentation](https://github.com/raphiniert-com/ra-data-postgrest/blob/master/README.md) for more details. + ### Authentication `ra-supabase` supports email/password and OAuth authentication. diff --git a/packages/ra-supabase-core/src/dataProvider.ts b/packages/ra-supabase-core/src/dataProvider.ts index 2d4f63a..79f1e1b 100644 --- a/packages/ra-supabase-core/src/dataProvider.ts +++ b/packages/ra-supabase-core/src/dataProvider.ts @@ -11,23 +11,30 @@ import { SupabaseClient } from '@supabase/supabase-js'; * @param instanceUrl The URL of the Supabase instance * @param apiKey The API key of the Supabase instance. Prefer the anonymous key. * @param supabaseClient The Supabase client + * @param defaultListOp Optional - The default list filter operator. Defaults to 'eq'. + * @param primaryKeys Optional - The primary keys of the tables. Defaults to 'id'. + * @param schema Optional - The custom schema to use. Defaults to none. * @returns A dataProvider for Supabase */ export const supabaseDataProvider = ({ instanceUrl, apiKey, supabaseClient, + httpClient = supabaseHttpClient({ apiKey, supabaseClient }), + defaultListOp = 'eq', + primaryKeys = defaultPrimaryKeys, + schema = defaultSchema, }: { instanceUrl: string; apiKey: string; supabaseClient: SupabaseClient; -}): DataProvider => { +} & Partial>): DataProvider => { const config: IDataProviderConfig = { apiUrl: `${instanceUrl}/rest/v1`, - httpClient: supabaseHttpClient({ apiKey, supabaseClient }), - defaultListOp: 'eq', - primaryKeys: defaultPrimaryKeys, - schema: defaultSchema, + httpClient, + defaultListOp, + primaryKeys, + schema, }; return postgrestRestProvider(config); }; diff --git a/packages/ra-supabase/README.md b/packages/ra-supabase/README.md index b76adca..4385c91 100644 --- a/packages/ra-supabase/README.md +++ b/packages/ra-supabase/README.md @@ -90,7 +90,7 @@ You must wrap your `` inside a `` as supabase use hash par ### DataProvider -`ra-supabase` is built on [`ra-data-postgrest`](https://github.com/raphiniert-com/ra-data-postgrest/tree/v2.0.0-alpha.0) that leverages [PostgREST](https://postgrest.org/en/stable/). As such, you have access the following features: +`ra-supabase` is built on [`ra-data-postgrest`](https://github.com/raphiniert-com/ra-data-postgrest/tree/v2.0.0) that leverages [PostgREST](https://postgrest.org/en/stable/). As such, you have access the following features: #### Filters operators @@ -115,6 +115,29 @@ See the [PostgREST documentation](https://postgrest.org/en/stable/api.html#opera As users authenticate through supabase, you can leverage [Row Level Security](https://supabase.com/docs/guides/auth/row-level-security). Users identity will be propagated through the dataProvider if you provided the public API (anon) key. Keep in mind that passing the `service_role` key will bypass Row Level Security. This is not recommended. +#### Customizing the dataProvider + +`supabaseDataProvider` also accepts the same options as the `ra-data-postgrest` dataProvider (except `apiUrl`), like [`primaryKeys`](https://github.com/raphiniert-com/ra-data-postgrest/blob/master/README.md#compound-primary-keys) or [`schema`](https://github.com/raphiniert-com/ra-data-postgrest/blob/master/README.md#custom-schema). + +```jsx +// in dataProvider.js +import { supabaseDataProvider } from 'ra-supabase-core'; +import { supabaseClient } from './supabase'; + +export const dataProvider = supabaseDataProvider({ + instanceUrl: 'YOUR_SUPABASE_URL', + apiKey: 'YOUR_SUPABASE_ANON_KEY', + supabaseClient, + primaryKeys: new Map([ + ['some_table', ['custom_id']], + ['another_table', ['first_column', 'second_column']], + ]), + schema: () => localStorage.getItem("schema") || "api", +}); +``` + +See the [`ra-data-postgrest`` documentation](https://github.com/raphiniert-com/ra-data-postgrest/blob/master/README.md) for more details. + ### Authentication `ra-supabase` supports email/password and OAuth authentication. From b531682d19a002038cf511f1cdc59b329ac9180d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Kaiser Date: Wed, 24 Jan 2024 11:48:23 +0100 Subject: [PATCH 3/4] improve contact list filters --- .../demo/src/contacts/ContactListFilter.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/demo/src/contacts/ContactListFilter.tsx b/packages/demo/src/contacts/ContactListFilter.tsx index 44ba16e..96b8fb5 100644 --- a/packages/demo/src/contacts/ContactListFilter.tsx +++ b/packages/demo/src/contacts/ContactListFilter.tsx @@ -15,9 +15,9 @@ import SupervisorAccountIcon from '@mui/icons-material/SupervisorAccount'; import { endOfYesterday, startOfWeek, - endOfWeek, startOfMonth, subMonths, + subWeeks, } from 'date-fns'; import { Status } from '../misc/Status'; @@ -53,17 +53,20 @@ export const ContactListFilter = () => { }} /> { }} /> Date: Wed, 24 Jan 2024 11:55:32 +0100 Subject: [PATCH 4/4] fix e2e tests --- cypress/e2e/lists.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/lists.cy.ts b/cypress/e2e/lists.cy.ts index 05b4ff4..6a8986b 100644 --- a/cypress/e2e/lists.cy.ts +++ b/cypress/e2e/lists.cy.ts @@ -17,7 +17,7 @@ describe('Lists', () => { getPaginationText().then(el => { const count = parseInt(el.text().split('of')[1].trim()); - cy.findByText('Before last month').click(); + cy.findByText('Earlier').click(); // Use should here to allow built-in retry as it may take a few ms for the list to update getPaginationText().should(el => { const countFiltered = parseInt(el.text().split('of')[1].trim());