@firecms/user_management 3.0.0-canary.84 → 3.0.0-canary.86

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,33 +1,25 @@
1
1
  import React, { useCallback, useEffect } from "react";
2
- import {
3
- addDoc,
4
- collection,
5
- deleteDoc,
6
- doc,
7
- DocumentSnapshot,
8
- getFirestore,
9
- onSnapshot,
10
- setDoc
11
- } from "@firebase/firestore";
12
- import { FirebaseApp } from "@firebase/app";
2
+ import equal from "react-fast-compare"
3
+
13
4
  import { UserManagement } from "../types";
14
- import { Authenticator, PermissionsBuilder, Role, User } from "@firecms/core";
5
+ import { Authenticator, DataSourceDelegate, Entity, PermissionsBuilder, Role, User } from "@firecms/core";
15
6
  import { resolveUserRolePermissions } from "../utils";
16
7
 
17
8
  type UserWithRoleIds = User & { roles: string[] };
18
9
 
19
10
  export interface UserManagementParams {
11
+
20
12
  /**
21
- * The Firebase app to use for the user management. The config will be saved in the Firestore
22
- * collection indicated by `configPath`.
13
+ * The delegate in charge of persisting the data.
23
14
  */
24
- firebaseApp?: FirebaseApp;
15
+ dataSourceDelegate?: DataSourceDelegate;
16
+
25
17
  /**
26
18
  * Path where the plugin users configuration is stored.
27
19
  * Default: __FIRECMS/config/users
28
20
  * You can specify a different path if you want to store the user management configuration in a different place.
29
21
  * Please keep in mind that the FireCMS users are not necessarily the same as the Firebase users (but they can be).
30
- * The path should be relative to the root of the Firestore database, and should always have an odd number of segments.
22
+ * The path should be relative to the root of the database, and should always have an odd number of segments.
31
23
  */
32
24
  usersPath?: string;
33
25
 
@@ -62,21 +54,23 @@ export interface UserManagementParams {
62
54
  /**
63
55
  * This hook is used to build a user management object that can be used to
64
56
  * manage users and roles in a Firestore backend.
65
- * @param backendFirebaseApp
57
+ * @param dataSourceDelegate
66
58
  * @param usersPath
67
59
  * @param rolesPath
68
60
  * @param usersLimit
69
61
  * @param canEditRoles
62
+ * @param allowDefaultRolesCreation
63
+ * @param includeCollectionConfigPermissions
70
64
  */
71
- export function useFirestoreUserManagement({
72
- firebaseApp,
73
- usersPath = "__FIRECMS/config/users",
74
- rolesPath = "__FIRECMS/config/roles",
75
- usersLimit,
76
- canEditRoles = true,
77
- allowDefaultRolesCreation,
78
- includeCollectionConfigPermissions
79
- }: UserManagementParams): UserManagement {
65
+ export function useBuildUserManagement({
66
+ dataSourceDelegate,
67
+ usersPath = "__FIRECMS/config/users",
68
+ rolesPath = "__FIRECMS/config/roles",
69
+ usersLimit,
70
+ canEditRoles = true,
71
+ allowDefaultRolesCreation,
72
+ includeCollectionConfigPermissions
73
+ }: UserManagementParams): UserManagement {
80
74
 
81
75
  const [rolesLoading, setRolesLoading] = React.useState<boolean>(true);
82
76
  const [usersLoading, setUsersLoading] = React.useState<boolean>(true);
@@ -94,62 +88,65 @@ export function useFirestoreUserManagement({
94
88
  const loading = rolesLoading || usersLoading;
95
89
 
96
90
  useEffect(() => {
97
- if (!firebaseApp || !rolesPath) return;
98
- const firestore = getFirestore(firebaseApp);
99
-
100
- return onSnapshot(collection(firestore, rolesPath),
101
- {
102
- next: (snapshot) => {
103
- setRolesError(undefined);
104
- try {
105
- const newRoles = docsToRoles(snapshot.docs);
91
+ if (!dataSourceDelegate || !rolesPath) return;
92
+ if (dataSourceDelegate.initialised !== undefined && !dataSourceDelegate.initialised) return;
93
+
94
+ return dataSourceDelegate.listenCollection?.({
95
+ path: rolesPath,
96
+ onUpdate(entities: Entity<any>[]): void {
97
+ setRolesError(undefined);
98
+ try {
99
+ const newRoles = entityToRoles(entities);
100
+ if (!equal(newRoles, roles))
106
101
  setRoles(newRoles);
107
- } catch (e) {
108
- console.error("Error loading roles", e);
109
- setRolesError(e as Error);
110
- }
111
- setRolesLoading(false);
112
- },
113
- error: (e) => {
102
+ } catch (e) {
114
103
  console.error("Error loading roles", e);
115
- setRolesError(e);
116
- setRolesLoading(false);
104
+ setRolesError(e as Error);
117
105
  }
106
+ setRolesLoading(false);
107
+ },
108
+ onError(e: any): void {
109
+ console.error("Error loading roles", e);
110
+ setRolesError(e);
111
+ setRolesLoading(false);
118
112
  }
119
- );
120
- }, [firebaseApp, rolesPath]);
113
+ });
114
+
115
+ }, [dataSourceDelegate, rolesPath]);
121
116
 
122
117
  useEffect(() => {
123
- if (!firebaseApp || !usersPath) return;
124
- const firestore = getFirestore(firebaseApp);
125
-
126
- return onSnapshot(collection(firestore, usersPath),
127
- {
128
- next: (snapshot) => {
129
- setUsersError(undefined);
130
- try {
131
- const newUsers = docsToUsers(snapshot.docs);
118
+ if (!dataSourceDelegate || !usersPath) return;
119
+ if (dataSourceDelegate.initialised !== undefined && !dataSourceDelegate.initialised) return;
120
+
121
+ return dataSourceDelegate.listenCollection?.({
122
+ path: usersPath,
123
+ onUpdate(entities: Entity<any>[]): void {
124
+ setUsersError(undefined);
125
+ try {
126
+ const newUsers = entitiesToUsers(entities);
127
+ if (!equal(newUsers, usersWithRoleIds))
132
128
  setUsersWithRoleIds(newUsers);
133
- } catch (e) {
134
- console.error("Error loading users", e);
135
- setUsersError(e as Error);
136
- }
137
- setUsersLoading(false);
138
- },
139
- error: (e) => {
129
+ } catch (e) {
140
130
  console.error("Error loading users", e);
141
- setUsersError(e);
142
- setUsersLoading(false);
131
+ setUsersError(e as Error);
143
132
  }
133
+ setUsersLoading(false);
134
+ },
135
+ onError(e: any): void {
136
+ console.error("Error loading users", e);
137
+ setUsersError(e);
138
+ setUsersLoading(false);
144
139
  }
145
- );
146
- }, [firebaseApp, usersPath]);
140
+ });
141
+
142
+ }, [dataSourceDelegate, usersPath]);
147
143
 
148
144
  const saveUser = useCallback(async (user: User): Promise<User> => {
149
- if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
150
- const firestore = getFirestore(firebaseApp);
151
- if (!firestore || !usersPath) throw Error("useFirestoreUserManagement Firestore not initialised");
145
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
146
+ if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
147
+
152
148
  console.debug("Persisting user", user);
149
+
153
150
  const roleIds = user.roles?.map(r => r.id);
154
151
  const {
155
152
  uid,
@@ -160,43 +157,64 @@ export function useFirestoreUserManagement({
160
157
  roles: roleIds
161
158
  };
162
159
  if (uid) {
163
- return setDoc(doc(firestore, usersPath, uid), data, { merge: true }).then(() => user);
160
+ return dataSourceDelegate.saveEntity({
161
+ status: "existing",
162
+ path: usersPath,
163
+ entityId: uid,
164
+ values: data
165
+ }).then(() => user);
164
166
  } else {
165
- return addDoc(collection(firestore, usersPath), data).then(() => user);
167
+ return dataSourceDelegate.saveEntity({
168
+ status: "new",
169
+ path: usersPath,
170
+ values: data
171
+ }).then(() => user);
166
172
  }
167
- }, [usersPath, firebaseApp]);
173
+ }, [usersPath, dataSourceDelegate]);
168
174
 
169
175
  const saveRole = useCallback((role: Role): Promise<void> => {
170
- if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
171
- const firestore = getFirestore(firebaseApp);
172
- if (!firestore || !rolesPath) throw Error("useFirestoreUserManagement Firestore not initialised");
176
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
177
+ if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
173
178
  console.debug("Persisting role", role);
174
179
  const {
175
180
  id,
176
181
  ...roleData
177
182
  } = role;
178
- const ref = doc(firestore, rolesPath, id);
179
- return setDoc(ref, roleData, { merge: true });
180
- }, [rolesPath, firebaseApp]);
183
+ return dataSourceDelegate.saveEntity({
184
+ status: "existing",
185
+ path: rolesPath,
186
+ entityId: id,
187
+ values: roleData
188
+ }).then(() => {
189
+ return;
190
+ });
191
+ }, [rolesPath, dataSourceDelegate]);
181
192
 
182
193
  const deleteUser = useCallback(async (user: User): Promise<void> => {
183
- if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
184
- const firestore = getFirestore(firebaseApp);
185
- if (!firestore || !usersPath) throw Error("useFirestoreUserManagement Firestore not initialised");
194
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
195
+ if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
186
196
  console.debug("Deleting", user);
187
197
  const { uid } = user;
188
- return deleteDoc(doc(firestore, usersPath, uid));
189
- }, [usersPath, firebaseApp]);
198
+ const entity: Entity<any> = {
199
+ path: usersPath,
200
+ id: uid,
201
+ values: {}
202
+ };
203
+ await dataSourceDelegate.deleteEntity({ entity })
204
+ }, [usersPath, dataSourceDelegate]);
190
205
 
191
- const deleteRole = useCallback((role: Role): Promise<void> => {
192
- if (!firebaseApp) throw Error("useFirestoreUserManagement Firebase not initialised");
193
- const firestore = getFirestore(firebaseApp);
194
- if (!firestore || !rolesPath) throw Error("useFirestoreUserManagement Firestore not initialised");
206
+ const deleteRole = useCallback(async (role: Role): Promise<void> => {
207
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
208
+ if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
195
209
  console.debug("Deleting", role);
196
210
  const { id } = role;
197
- const ref = doc(firestore, rolesPath, id);
198
- return deleteDoc(ref);
199
- }, [rolesPath, firebaseApp]);
211
+ const entity: Entity<any> = {
212
+ path: usersPath,
213
+ id: id,
214
+ values: {}
215
+ };
216
+ await dataSourceDelegate.deleteEntity({ entity })
217
+ }, [rolesPath, dataSourceDelegate]);
200
218
 
201
219
  const collectionPermissions: PermissionsBuilder = useCallback(({
202
220
  collection,
@@ -256,22 +274,22 @@ export function useFirestoreUserManagement({
256
274
  }
257
275
  }
258
276
 
259
- const docsToUsers = (docs: DocumentSnapshot[]): (UserWithRoleIds)[] => {
277
+ const entitiesToUsers = (docs: Entity<Omit<UserWithRoleIds, "id">>[]): (UserWithRoleIds)[] => {
260
278
  return docs.map((doc) => {
261
- const data = doc.data() as any;
279
+ const data = doc.values as any;
262
280
  const newVar = {
263
281
  uid: doc.id,
264
282
  ...data,
265
- created_on: data?.created_on?.toDate(),
266
- updated_on: data?.updated_on?.toDate()
283
+ created_on: data?.created_on,
284
+ updated_on: data?.updated_on
267
285
  };
268
286
  return newVar as (UserWithRoleIds);
269
287
  });
270
288
  }
271
289
 
272
- const docsToRoles = (docs: DocumentSnapshot[]): Role[] => {
273
- return docs.map((doc) => ({
290
+ const entityToRoles = (entities: Entity<Omit<Role, "id">>[]): Role[] => {
291
+ return entities.map((doc) => ({
274
292
  id: doc.id,
275
- ...doc.data()
293
+ ...doc.values
276
294
  } as Role));
277
295
  }