Skip to content

Commit 8c7c847

Browse files
committed
Add documentation for headless enterprise features in ra-core documentation
1 parent 353b0e2 commit 8c7c847

23 files changed

+1790
-2
lines changed

docs_headless/astro.config.mjs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ export default defineConfig({
106106
'usepermissions',
107107
'addrefreshauthtoauthprovider',
108108
'addrefreshauthtodataprovider',
109+
enterpriseEntry('canAccessWithPermissions'),
110+
enterpriseEntry('getPermissionsFromRoles'),
109111
],
110112
},
111113
{
@@ -203,6 +205,28 @@ export default defineConfig({
203205
'usegetrecordrepresentation',
204206
],
205207
},
208+
{
209+
label: 'Realtime',
210+
items: [
211+
enterpriseEntry('usePublish'),
212+
enterpriseEntry('useSubscribe'),
213+
enterpriseEntry('useSubscribeCallback'),
214+
enterpriseEntry('useSubscribeToRecord'),
215+
enterpriseEntry('useSubscribeToRecordList'),
216+
enterpriseEntry('useLock'),
217+
enterpriseEntry('useUnlock'),
218+
enterpriseEntry('useGetLock'),
219+
enterpriseEntry('useGetLockLive'),
220+
enterpriseEntry('useGetLocks'),
221+
enterpriseEntry('useGetLocksLive'),
222+
enterpriseEntry('useLockCallbacks'),
223+
enterpriseEntry('useLockOnMount'),
224+
enterpriseEntry('useLockOnCall'),
225+
enterpriseEntry('useGetListLive'),
226+
enterpriseEntry('useGetOneLive'),
227+
enterpriseEntry('<LockStatusBase>'),
228+
],
229+
},
206230
{
207231
label: 'Recipes',
208232
items: ['caching', 'unittesting'],
@@ -240,3 +264,19 @@ export default defineConfig({
240264
assets: 'assets',
241265
},
242266
});
267+
268+
/**
269+
* @param {string} name
270+
* @returns {any}
271+
*/
272+
function enterpriseEntry(name) {
273+
return {
274+
link: name.toLowerCase().replace(/</g, '').replace(/>/g, ''),
275+
label: name,
276+
attrs: { class: 'enterprise' },
277+
badge: {
278+
text: 'React Admin Enterprise',
279+
variant: 'default',
280+
},
281+
};
282+
}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
---
2+
title: "<LockStatusBase>"
3+
---
4+
5+
**Tip**: `ra-core-ee` is part of the [React-Admin Enterprise Edition](https://marmelab.com/ra-enterprise/), and hosted in a private npm registry. You need to subscribe to one of the Enterprise Edition plans to access this package.
6+
7+
Use the `<LockStatusBase>` component to display the lock status of the record in the nearest `RecordContext`:
8+
9+
```tsx
10+
import React from 'react';
11+
import { Lock, LockOpen, LoaderCircle } from 'lucide-react';
12+
import { LockStatusBase } from '@react-admin/ra-core-ee';
13+
14+
export const LockStatus = () => {
15+
return (
16+
<LockStatusBase
17+
{...props}
18+
render={({
19+
doLock,
20+
doUnlock,
21+
isLocking,
22+
isPending,
23+
isUnlocking,
24+
lockStatus,
25+
message,
26+
}) => {
27+
if (isPending) {
28+
return null;
29+
}
30+
31+
if (lockStatus === 'lockedByUser') {
32+
return (
33+
<button
34+
title={message}
35+
disabled={isUnlocking}
36+
onClick={(
37+
e: React.MouseEvent<HTMLButtonElement>
38+
) => {
39+
e.stopPropagation();
40+
doUnlock();
41+
}}
42+
>
43+
{isUnlocking ? (
44+
<LoaderCircle className="h-4 w-4 animate-spin" />
45+
) : (
46+
<Lock className="h-4 w-4" />
47+
)}
48+
</button>
49+
);
50+
}
51+
if (lockStatus === 'lockedByAnotherUser') {
52+
return (
53+
<Lock className="h-4 w-4 text-error" />
54+
);
55+
}
56+
if (lockStatus === 'unlocked') {
57+
return (
58+
<button
59+
title={message}
60+
disabled={isLocking}
61+
onClick={(
62+
e: React.MouseEvent<HTMLButtonElement>
63+
) => {
64+
e.stopPropagation();
65+
doLock();
66+
}}
67+
color="warning"
68+
>
69+
{isLocking ? (
70+
<LoaderCircle className="h-4 w-4 animate-spin" />
71+
) : (
72+
<LockOpen className="h-4 w-4" />
73+
)}
74+
</button>
75+
);
76+
}
77+
return null;
78+
}}
79+
/>
80+
);
81+
};
82+
```
83+
84+
In addition to the [`useLockCallbacks`](#uselockcallbacks) parameters, `<LockStatusBase>` accepts a `render` prop. The function passed to the `render` prop will be called with the result of the `useLockCallbacks` hook.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
title: "canAccessWithPermissions"
3+
---
4+
5+
**Tip**: `ra-core-ee` is part of the [React-Admin Enterprise Edition](https://marmelab.com/ra-enterprise/), and hosted in a private npm registry. You need to subscribe to one of the Enterprise Edition plans to access this package.
6+
7+
`canAccessWithPermissions` is a helper function that facilitates the `authProvider.canAccess()` method implementation:
8+
9+
## Usage
10+
11+
The user roles and permissions should be returned upon login. The `authProvider` should store the permissions in memory, or in localStorage. This allows `authProvider.canAccess()` to read the permissions from localStorage.
12+
13+
```tsx
14+
// in roleDefinitions.ts
15+
export const roleDefinitions = {
16+
admin: [
17+
{ action: '*', resource: '*' }
18+
],
19+
reader: [
20+
{ action: ['list', 'show', 'export'], resource: '*' }
21+
{ action: 'read', resource: 'posts.*' }
22+
{ action: 'read', resource: 'comments.*' }
23+
],
24+
accounting: [
25+
{ action: '*', resource: 'sales' },
26+
],
27+
};
28+
29+
// in authProvider.ts
30+
import { canAccessWithPermissions, getPermissionsFromRoles } from '@react-admin/ra-core-ee';
31+
import { roleDefinitions } from './roleDefinitions';
32+
33+
const authProvider = {
34+
login: async ({ username, password }) => {
35+
const request = new Request('https://mydomain.com/authenticate', {
36+
method: 'POST',
37+
body: JSON.stringify({ username, password }),
38+
headers: new Headers({ 'Content-Type': 'application/json' }),
39+
});
40+
const response = await fetch(request);
41+
if (response.status < 200 || response.status >= 300) {
42+
throw new Error(response.statusText);
43+
}
44+
const { user: { roles, permissions }} = await response.json();
45+
// merge the permissions from the roles with the extra permissions
46+
const permissions = getPermissionsFromRoles({
47+
roleDefinitions,
48+
userPermissions,
49+
userRoles
50+
});
51+
localStorage.setItem('permissions', JSON.stringify(permissions));
52+
},
53+
canAccess: async ({ action, resource, record }) => {
54+
const permissions = JSON.parse(localStorage.getItem('permissions'));
55+
return canAccessWithPermissions({
56+
permissions,
57+
action,
58+
resource,
59+
record,
60+
});
61+
}
62+
// ...
63+
};
64+
```
65+
66+
## Parameters
67+
68+
This function takes an object as argument with the following fields:
69+
70+
| Name | Optional | Type | Description
71+
| - | - | - | - |
72+
| `permissions` | Required | `Array<Permission>` | An array of permissions for the current user
73+
| `action` | Required | `string` | The action for which to check users has the execution right
74+
| `resource` | Required | `string` | The resource for which to check users has the execution right
75+
| `record` | Required | `string` | The record for which to check users has the execution right
76+
77+
`canAccessWithPermissions` expects the `permissions` to be a flat array of permissions. It is your responsibility to fetch these permissions (usually during login). If the permissions are spread into several role definitions, you can merge them into a single array using the [`getPermissionsFromRoles`](#getpermissionsfromroles) function.
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
---
2+
title: "getPermissionsFromRoles"
3+
---
4+
5+
**Tip**: `ra-core-ee` is part of the [React-Admin Enterprise Edition](https://marmelab.com/ra-enterprise/), and hosted in a private npm registry. You need to subscribe to one of the Enterprise Edition plans to access this package.
6+
7+
This function returns an array of user permissions based on a role definition, a list of roles, and a list of user permissions. It merges the permissions defined in `roleDefinitions` for the current user's roles (`userRoles`) with the extra `userPermissions`.
8+
9+
```ts
10+
import { getPermissionsFromRoles } from '@react-admin/ra-core-ee';
11+
12+
// static role definitions (usually in the app code)
13+
const roleDefinitions = {
14+
admin: [
15+
{ action: '*', resource: '*' }
16+
],
17+
reader: [
18+
{ action: ['list', 'show', 'export'], resource: '*' }
19+
{ action: 'read', resource: 'posts.*' }
20+
{ action: 'read', resource: 'comments.*' }
21+
],
22+
accounting: [
23+
{ action: '*', resource: 'sales' },
24+
],
25+
};
26+
27+
const permissions = getPermissionsFromRoles({
28+
roleDefinitions,
29+
// roles of the current user (usually returned by the server upon login)
30+
userRoles: ['reader'],
31+
// extra permissions for the current user (usually returned by the server upon login)
32+
userPermissions: [
33+
{ action: 'list', resource: 'sales'},
34+
],
35+
});
36+
// permissions = [
37+
// { action: ['list', 'show', 'export'], resource: '*' },
38+
// { action: 'read', resource: 'posts.*' },
39+
// { action: 'read', resource: 'comments.*' },
40+
// { action: 'list', resource: 'sales' },
41+
// ];
42+
```
43+
44+
## Usage
45+
46+
The user roles and permissions should be returned upon login. The `authProvider` should store the permissions in memory, or in localStorage. This allows `authProvider.canAccess()` to read the permissions from localStorage.
47+
48+
```tsx
49+
// in roleDefinitions.ts
50+
export const roleDefinitions = {
51+
admin: [
52+
{ action: '*', resource: '*' }
53+
],
54+
reader: [
55+
{ action: ['list', 'show', 'export'], resource: '*' }
56+
{ action: 'read', resource: 'posts.*' }
57+
{ action: 'read', resource: 'comments.*' }
58+
],
59+
accounting: [
60+
{ action: '*', resource: 'sales' },
61+
],
62+
};
63+
64+
// in authProvider.ts
65+
import { canAccessWithPermissions, getPermissionsFromRoles } from '@react-admin/ra-core-ee';
66+
import { roleDefinitions } from './roleDefinitions';
67+
68+
const authProvider = {
69+
login: async ({ username, password }) => {
70+
const request = new Request('https://mydomain.com/authenticate', {
71+
method: 'POST',
72+
body: JSON.stringify({ username, password }),
73+
headers: new Headers({ 'Content-Type': 'application/json' }),
74+
});
75+
const response = await fetch(request);
76+
if (response.status < 200 || response.status >= 300) {
77+
throw new Error(response.statusText);
78+
}
79+
const { user: { roles, permissions }} = await response.json();
80+
// merge the permissions from the roles with the extra permissions
81+
const permissions = getPermissionsFromRoles({
82+
roleDefinitions,
83+
userPermissions,
84+
userRoles
85+
});
86+
localStorage.setItem('permissions', JSON.stringify(permissions));
87+
},
88+
canAccess: async ({ action, resource, record }) => {
89+
const permissions = JSON.parse(localStorage.getItem('permissions'));
90+
return canAccessWithPermissions({
91+
permissions,
92+
action,
93+
resource,
94+
record,
95+
});
96+
}
97+
// ...
98+
};
99+
```
100+
101+
## Parameters
102+
103+
This function takes an object as argument with the following fields:
104+
105+
| Name | Optional | Type | Description
106+
| - | - | - | - |
107+
| `roleDefinitions` | Required | `Record<string, Permission>` | A dictionary containing the role definition for each role
108+
| `userRoles` | Optional | `Array<string>` | An array of roles (admin, reader...) for the current user
109+
| `userPermissions` | Optional | `Array<Permission>` | An array of permissions for the current user
110+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: "useGetListLive"
3+
---
4+
5+
**Tip**: `ra-core-ee` is part of the [React-Admin Enterprise Edition](https://marmelab.com/ra-enterprise/), and hosted in a private npm registry. You need to subscribe to one of the Enterprise Edition plans to access this package.
6+
7+
Alternative to `useGetList` that subscribes to live updates on the record list.
8+
9+
```tsx
10+
import { useGetListLive } from '@react-admin/ra-core-ee';
11+
12+
const LatestNews = () => {
13+
const { data, total, isLoading, error } = useGetListLive('posts', {
14+
pagination: { page: 1, perPage: 10 },
15+
sort: { field: 'published_at', order: 'DESC' },
16+
});
17+
if (isLoading) {
18+
return <div>Loading...</div>;
19+
}
20+
if (error) {
21+
return <p>ERROR</p>;
22+
}
23+
24+
return (
25+
<ul>
26+
{data.map(item => (
27+
<li key={item.id}>{item.title}</li>
28+
))}
29+
</ul>
30+
);
31+
};
32+
```
33+
34+
The hook will subscribe to live updates on the list of records (topic: `resource/[resource]`) and will refetch the list when a new record is created, or an existing record is updated or deleted.
35+
36+
See the [useGetList](https://marmelab.com/react-admin/useGetList.html) documentation for the full list of parameters and return type.

0 commit comments

Comments
 (0)