@firecms/user_management 3.0.0-beta.8 → 3.0.0-canary.100

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,69 @@ 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) {
103
+ setRoles([]);
114
104
  console.error("Error loading roles", e);
115
- setRolesError(e);
116
- setRolesLoading(false);
105
+ setRolesError(e as Error);
117
106
  }
107
+ setRolesLoading(false);
108
+ },
109
+ onError(e: any): void {
110
+ setRoles([]);
111
+ console.error("Error loading roles", e);
112
+ setRolesError(e);
113
+ setRolesLoading(false);
118
114
  }
119
- );
120
- }, [firebaseApp, rolesPath]);
115
+ });
116
+
117
+ }, [dataSourceDelegate?.initialised, rolesPath]);
121
118
 
122
119
  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);
120
+ if (!dataSourceDelegate || !usersPath) return;
121
+ if (dataSourceDelegate.initialised !== undefined && !dataSourceDelegate.initialised) return;
122
+
123
+ return dataSourceDelegate.listenCollection?.({
124
+ path: usersPath,
125
+ onUpdate(entities: Entity<any>[]): void {
126
+ setUsersError(undefined);
127
+ try {
128
+ const newUsers = entitiesToUsers(entities);
129
+ if (!equal(newUsers, usersWithRoleIds))
132
130
  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) => {
131
+ } catch (e) {
132
+ setUsersWithRoleIds([]);
140
133
  console.error("Error loading users", e);
141
- setUsersError(e);
142
- setUsersLoading(false);
134
+ setUsersError(e as Error);
143
135
  }
136
+ setUsersLoading(false);
137
+ },
138
+ onError(e: any): void {
139
+ setUsersWithRoleIds([]);
140
+ console.error("Error loading users", e);
141
+ setUsersError(e);
142
+ setUsersLoading(false);
144
143
  }
145
- );
146
- }, [firebaseApp, usersPath]);
144
+ });
145
+
146
+ }, [dataSourceDelegate?.initialised, usersPath]);
147
147
 
148
148
  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");
149
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
150
+ if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
151
+
152
152
  console.debug("Persisting user", user);
153
+
153
154
  const roleIds = user.roles?.map(r => r.id);
154
155
  const {
155
156
  uid,
@@ -160,43 +161,64 @@ export function useFirestoreUserManagement({
160
161
  roles: roleIds
161
162
  };
162
163
  if (uid) {
163
- return setDoc(doc(firestore, usersPath, uid), data, { merge: true }).then(() => user);
164
+ return dataSourceDelegate.saveEntity({
165
+ status: "existing",
166
+ path: usersPath,
167
+ entityId: uid,
168
+ values: data
169
+ }).then(() => user);
164
170
  } else {
165
- return addDoc(collection(firestore, usersPath), data).then(() => user);
171
+ return dataSourceDelegate.saveEntity({
172
+ status: "new",
173
+ path: usersPath,
174
+ values: data
175
+ }).then(() => user);
166
176
  }
167
- }, [usersPath, firebaseApp]);
177
+ }, [usersPath, dataSourceDelegate?.initialised]);
168
178
 
169
179
  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");
180
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
181
+ if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
173
182
  console.debug("Persisting role", role);
174
183
  const {
175
184
  id,
176
185
  ...roleData
177
186
  } = role;
178
- const ref = doc(firestore, rolesPath, id);
179
- return setDoc(ref, roleData, { merge: true });
180
- }, [rolesPath, firebaseApp]);
187
+ return dataSourceDelegate.saveEntity({
188
+ status: "existing",
189
+ path: rolesPath,
190
+ entityId: id,
191
+ values: roleData
192
+ }).then(() => {
193
+ return;
194
+ });
195
+ }, [rolesPath, dataSourceDelegate?.initialised]);
181
196
 
182
197
  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");
198
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
199
+ if (!usersPath) throw Error("useBuildUserManagement Firestore not initialised");
186
200
  console.debug("Deleting", user);
187
201
  const { uid } = user;
188
- return deleteDoc(doc(firestore, usersPath, uid));
189
- }, [usersPath, firebaseApp]);
202
+ const entity: Entity<any> = {
203
+ path: usersPath,
204
+ id: uid,
205
+ values: {}
206
+ };
207
+ await dataSourceDelegate.deleteEntity({ entity })
208
+ }, [usersPath, dataSourceDelegate?.initialised]);
190
209
 
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");
210
+ const deleteRole = useCallback(async (role: Role): Promise<void> => {
211
+ if (!dataSourceDelegate) throw Error("useBuildUserManagement Firebase not initialised");
212
+ if (!rolesPath) throw Error("useBuildUserManagement Firestore not initialised");
195
213
  console.debug("Deleting", role);
196
214
  const { id } = role;
197
- const ref = doc(firestore, rolesPath, id);
198
- return deleteDoc(ref);
199
- }, [rolesPath, firebaseApp]);
215
+ const entity: Entity<any> = {
216
+ path: usersPath,
217
+ id: id,
218
+ values: {}
219
+ };
220
+ await dataSourceDelegate.deleteEntity({ entity })
221
+ }, [rolesPath, dataSourceDelegate?.initialised]);
200
222
 
201
223
  const collectionPermissions: PermissionsBuilder = useCallback(({
202
224
  collection,
@@ -256,22 +278,22 @@ export function useFirestoreUserManagement({
256
278
  }
257
279
  }
258
280
 
259
- const docsToUsers = (docs: DocumentSnapshot[]): (UserWithRoleIds)[] => {
281
+ const entitiesToUsers = (docs: Entity<Omit<UserWithRoleIds, "id">>[]): (UserWithRoleIds)[] => {
260
282
  return docs.map((doc) => {
261
- const data = doc.data() as any;
283
+ const data = doc.values as any;
262
284
  const newVar = {
263
285
  uid: doc.id,
264
286
  ...data,
265
- created_on: data?.created_on?.toDate(),
266
- updated_on: data?.updated_on?.toDate()
287
+ created_on: data?.created_on,
288
+ updated_on: data?.updated_on
267
289
  };
268
290
  return newVar as (UserWithRoleIds);
269
291
  });
270
292
  }
271
293
 
272
- const docsToRoles = (docs: DocumentSnapshot[]): Role[] => {
273
- return docs.map((doc) => ({
294
+ const entityToRoles = (entities: Entity<Omit<Role, "id">>[]): Role[] => {
295
+ return entities.map((doc) => ({
274
296
  id: doc.id,
275
- ...doc.data()
297
+ ...doc.values
276
298
  } as Role));
277
299
  }